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).VCCorsel.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!
Generic Net Selectors
You can also create custom net selectors using TypeScript generics:
import { sel } from "tscircuit"
// Define custom net names using generics
const customNets = sel.net<"CUSTOMNET1" | "CUSTOMNET2">()
customNets.CUSTOMNET1 // "net.CUSTOMNET1"
customNets.CUSTOMNET2 // "net.CUSTOMNET2"
// TypeScript will error on non-existent nets!
// customNets.DOES_NOT_EXIST // TypeScript error!
Collecting Typed Nets
You may want to group your typed nets alongside other reusable selectors:
import { sel } from "tscircuit"
const selectors = {
net: sel.net<"GND" | "VCC" | "V5">(),
// ...other selectors
}
selectors.net.GND // "net.GND"
Dynamic Reference Designators
You can call sel as a function to create selectors for any reference designator.
It's best to assign the resulting selector set to a variable so that you keep
type safety throughout your code:
import { sel } from "tscircuit"
import { RP2040 } from "./rp2040"
const selU1 = sel.U1(RP2040)
// More explicit version for custom scenarios
const selSJ1 = sel<"A" | "B" | "C">("SJ1")
selSJ1.A // Returns ".SJ1 > .A"
So sel is a more type-safe, conventional way of writing
port selectors.