Using "sel" References
The sel
object is a special import that allows you to easily reference
components with a more type-safety than a string. It can also be more ergonomic
than typing out a port selector.
There are many ways to use sel
, the most common are:
- Conventional References
sel.C1.pos
- Type-Safe Chip Pin References
sel.U1(MyChip).VCC
- Type-Safe Module References
sel.M1(MyModule).VCC
orsel.M1(MyModule).U1.D0
Conventional References
Conventional references are the most basic way to use sel
. There is a large,
built-in mapping of conventional strings to reference components. When you
start typing sel.
, you will see a list of the available references.
import { sel } from "tscircuit"
export default () => (
<board width="10mm" height="10mm">
<resistor
resistance="1k"
footprint="0402"
name="R1"
/>
<capacitor
capacitance="1000pF"
footprint="0402"
name="C1"
/>
<trace from={sel.R1.pin1} to={sel.C1.pos} />
</board>
)
The sel
can be thought of as a very large mapping of conventional strings.
Here are some sel
expressions and their corresponding string:
import { sel } from "tscircuit"
sel.R1.pin1
// ".R1 > .pin1"
sel.C1.pos
// ".C1 > .pos"
sel.net.GND
// "net.GND"
sel.U1.GPIO1
// ".U1 > .GPIO1"
Type-Safe Chip Pin References
The sel
object provides enhanced type safety when working with chips by allowing you to reference pins with proper TypeScript types. There are two advanced ways to use sel
with chips:
You can pass a chip component to sel
to get fully type-safe pin accessors:
import { MyChip } from "./my-chip"
const selectors = {
U1: sel.U1(MyChip)
}
export const MyChipWithSomeTraces = () => (
<group>
<MyChip name="U1" />
<trace from={selectors.U1.VCC} to="net.VCC" />
<trace from={selectors.U1.GND} to="net.GND" />
</group>
)
In order for type-safe selectors to work, you need to define your chip component
with a ChipProps
type, like this:
import { ChipProps } from "tscircuit"
const pinLabels = {
pin1: "VCC",
pin2: "GND",
pin3: "DATA1",
pin4: "DATA2",
} as const
const MyChip = (props: ChipProps<typeof pinLabels>) => (
<chip {...props} pinLabels={pinLabels} />
)
sel.U1(MyChip).VCC
is the same as sel.U1.VCC
,
however, when we pass MyChip
to sel.U1
, TypeScript will check that MyChip
has a pin called VCC
and will error if it doesn't.
import { sel } from "tscircuit"
import { MyChip } from "./my-chip"
sel.U1(MyChip).VCC
// ".U1 > .VCC"
sel.U1(MyChip).GND
// ".U1 > .GND"
// TypeScript will error on non-existent pins!
// sel.U1(MyChip).DOES_NOT_EXIST -> ERROR!
Type-Safe Module References
Similarly, you can use sel
with modules that define connections
or selectors
in their props. This provides type-safety for accessing the exposed ports or nested selectors of a module instance.
Using sel
with connections
prop
If a module defines a connections
prop, you can pass the module component to sel
to get type-safe accessors for its connections:
import { sel } from "tscircuit"
// Define a simple module that uses the `connections` prop
const MyModuleWithConnections = (props: {
name: string
connections: {
GND: string
VCC?: string // Optional connection
}
}) => (
<group>
<resistor name="R1" resistance="1k" connections={{ GND: props.connections.GND }} />
{/** etc. **/}
</group>
)
const selM1 = sel.M1(MyModuleWithConnections)
selM1.GND // Returns ".M1 > .GND"
selM1.VCC // Returns ".M1 > .VCC"
// TypeScript will error on non-existent connection keys!
// selM1.INVALID_KEY // TypeScript error!
Using sel
with selectors
prop
If a module defines a selectors
prop, you can pass the module component to sel
to get type-safe accessors for its nested selectors and their connections:
import { sel } from "tscircuit"
// Define a simple module that uses the `selectors` prop
const MyModuleWithSelectors = (props: {
name: string
selectors: {
U1: { GND: string; VCC?: string }
R1: { pin1: string; pin2: string }
}
}) => (
<group>
<chip name="U1" connections={props.selectors.U1} />
<resistor name="R1" resistance="1k" connections={props.selectors.R1} />
</group>
)
const selM2 = sel.M2(MyModuleWithSelectors)
selM2.U1.GND // Returns ".M2 > .U1 > .GND"
selM2.U1.VCC // Returns ".M2 > .U1 > .VCC"
selM2.R1.pin1 // Returns ".M2 > .R1 > .pin1"
selM2.R1.pin2 // Returns ".M2 > .R1 > .pin2"
// TypeScript will error on non-existent selector keys!
// selM2.U2 // TypeScript error!
// TypeScript will error on non-existent connection keys within a selector!
// selM2.U1.INVALID_KEY // TypeScript error!
Custom Usage
Generic Pin Selectors (no chip needed!)
You can also use TypeScript generics to define custom pin types directly:
import { sel } from "tscircuit"
// Define custom pin types using generics
const selU2 = sel.U2<"custompin1" | "custompin2">()
selU2.custompin1 // Returns ".U2 > .custompin1"
selU2.custompin2 // Returns ".U2 > .custompin2"
// TypeScript will error on non-existent pins!
// selU2.doesnotexist // TypeScript error!
So sel
is a more type-safe, conventional way of writing
port selectors.