Typescript: Hur filtrerar du ett typs egenskaper till de av en viss typ?


Jag har ett gränssnitt

export interface MyInterface {
    a: number;
    b: string;
    c: number;
}

Jag vill skapa en bokstavlig typ av egenskapsnamnen för vilka värdet är av typen number

Jag vet hur man får typen med alla egenskapsnamn med hjälp av

type MyType = keyof MyInterface // gives 'a'|'b'|'c'

Jag vill få bara 'a'|'c'

Svar #1

Du kan verkligen definiera en sådan typ i TypeScript:

type KeysMatching<T extends object, V> = {
  [K in keyof T]-?: T[K] extends V ? K : never
}[keyof T];

type MyType = KeysMatching<MyInterface, number>;
// type MyType = "a" | "c"

I detta returnerar KeysMatching<T, V> mängden nycklar i T vars egenskaper kan tilldelas V. Den använder en konditionell och mappad typ tillsammans med en egenskapsuppslagning. För varje nyckel K i keyof T kontrollerar den om T[K] kan tilldelas V. Om så är fallet returnerar den nyckeln K; om inte, returnerar den never. Så för dina typer skulle det vara något i stil med {a: "a", b: never, c: "c"}. Sedan slår vi upp egenskapsvärdena och får en union av typerna som "a" | never | "c" vilket reduceras till "a" | "c", precis som du ville.

Observera att KeysMatching<T, V> endast returnerar de egenskapsnycklar vars värden matchar V när man läser egenskapen. De som är exakt V eller en underordnad typ av V:

interface AnotherInterface {
  narrower: 1;
  exact: number;
  wider: string | number;
}

type AnotherType = KeysMatching<AnotherInterface, number>;
// type AnotherType = "narrower" | "exact"

Om du vill få nycklarna som matchar V när du skriver en egenskap av T... det vill säga, exakt V eller en supertyp av V, då behöver du en annan implementation av KeysMatching:

type KeysMatchingWrite<T extends object, V> = {
  [K in keyof T]-?: [V] extends [T[K]] ? K : never
}[keyof T];

type AnotherTypeWrite = KeysMatchingWrite<AnotherInterface, number>;
// type AnotherTypeWrite = "exact" | "wider"

Hur som helst, hoppas det hjälper. Lycka till!

Länk till kod

Relaterade artiklar