我想编写一个通用函数,该函数以通用类型的属性名称作为参数。我需要使TypeScript断言该属性的值是特定类型的。考虑下面的简化代码:
interface MyObject {
myProp: number;
mySecondProp: string;
myOtherProp: string;
myFlagProp: boolean;
}
const doStuff<T> = (obj: T, propName: SomeType) => { /***/ }
我可以提取特定类型(例如string
)的所有属性,但是编译器将无法确切知道该属性的类型:
type StringProps<T> = {
[K in keyof T]: T[K] extends string ? K : never }[keyof T]
}
const doStuff<T> = (obj: T, propName: StringProps<T>) => {
obj[propName].indexOf("a"); // Property 'indexOf' does not exist on type 'T[{ [K in keyof T]: T[K] extends string ? K : never; }[keyof T]]'
}
如何使编译器理解它应该只接受字符串属性名称,并且该值始终为string
类型?
答案 0 :(得分:3)
Typescript通常不能很好地推断出仍然包含未解析类型参数的条件类型。因此,当您调用StringProps<T>
可以轻松地解析为函数中T
的任何字符串键时,编译器将不会尝试感知T[StringProps<T>]
。
您可以用其他方式指定参数。您可以将另一个类型参数用作属性键,并指定T
必须是Record<K, string>
。对于编译器来说,这样做更简单:
type StringProps<T> = { [K in keyof T]: T[K] extends string ? K : never }[keyof T];
function doStuff<T>(obj: T, propName: StringProps<T>): void
function doStuff<K extends PropertyKey, T extends Record<K, string>>(obj: T, propName: K): void {
obj[propName].indexOf("a");
}
您会注意到,我也保留了您的签名,一个对呼叫站点的智能表现更好,对于新的实现,新的签名表现更好。
取决于您在函数中执行的索引编制数量,您最好只使用一个断言:
type StringProps<T> = { [K in keyof T]: T[K] extends string ? K : never }[keyof T];
function doStuff<T>(obj: T, propName: StringProps<T>): void{
(obj[propName] as unknown as string).indexOf("a");
}