I have a interface
export interface MyInterface {
a: number;
b: string;
c: number;
}
I want to create a literal type of the property names for which value is of type number
I know how to get the type with all property names using
type MyType = keyof MyInterface // gives 'a'|'b'|'c'
I want to get just 'a'|'c'
You certainly can define such a type in 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"
In this, KeysMatching<T, V> returns the set of keys of T whose properties are assignable to V. It uses a conditional and mapped type along with a property lookup. For each key K in keyof T, it checks whether T[K] is assignable to V. If so, it returns the key K; if not, it returns never. So for your types it would be something like {a: "a", b: never, c: "c"}. Then we look up the property values and get a union of the types like "a" | never | "c" which reduces to "a" | "c", exactly as you wanted.
Do note that KeysMatching<T, V> only returns those property keys whose values match V when reading the property. Those that are exactly V or a subtype of V:
interface AnotherInterface {
narrower: 1;
exact: number;
wider: string | number;
}
type AnotherType = KeysMatching<AnotherInterface, number>;
// type AnotherType = "narrower" | "exact"
If you want to get the keys which match V when writing a property of T... that is, exactly V or a supertype of V, then you need a different implementation of 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"
Anyway, hope that helps. Good luck!
Link to code
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With