When it comes to TypeScript, object types have a unique set of keys that are either known or defined by an index signature. Known keys are specific string or number literals, while index signature keys can represent multiple possible keys at once. With the introduction of pattern template literals and symbols in index signatures, the flexibility has increased significantly.
type Foo = {
[k: string]: 0 | 1
x: 0,
y: 1
}
In this example, "Foo" has two known keys - "x" and "y", along with one index signature key "string". Similarly, the type "MyInterface" includes four known keys and inherits a "string" index signature key from extending Record.
The use of the keyof operator helps create a union of all keys within a type. For instance, for "Foo", the result is "x" | "y" | string. On the other hand, for "MyInterface", it becomes "Appearance" | "Size" | "Color" | "Block" | string.
Since string literals are subtypes of strings, when combined, they reduce to just "string." This means that using keyof on types like "Foo" and "MyInterface" will only yield "string," as it encompasses all the string literal keys.
Therefore, extracting only known keys without interference from index signature keys can be achieved through strategic refactoring:
interface MyKnownInterface {
Appearance?: "default" | "primary" | "link";
Size?: "small" | "medium" | "large";
Color?: string;
Block?: boolean;
}
interface MyInterface extends Record<string, any>, MyKnownInterface { }
type KK = (keyof MyKnownInterface) & string
type MyInterfaceKeys = (keyof MyKnownInterface)[]
By maintaining a separate interface for known keys and leveraging type manipulations, a cleaner separation between known and index signature keys can be achieved.
Alternatively, utilizing techniques like key remapping in mapped types can suppress unwanted index signatures and focus solely on known keys:
type KnownKeys<T> = keyof {
[K in keyof T as {} extends { [P in K]: any } ? never : K]: never
}
type MyInterfaceKeys = (KnownKeys<MyInterface>)[]
These methods ensure a clear distinction between known keys and index signature keys in TypeScript interfaces, enhancing code readability and maintainability. The code snippet provided illustrates how these concepts can be implemented effectively.