Skip to main content

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

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>
)
info

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.