我有一些情形,其中我有挥动字符串,数字和布尔值,并且返回与正确类型的值的吸气剂的对象,例如是这样的:
interface AllowedMapTypings {
'str': string;
'lon': number;
'str2': string;
}
const obj: AllowedMapTypings = {
'str': 'foo',
'lon': 123,
'str2': 'foo'
};
function foo<T extends keyof AllowedMapTypings>(key: T): AllowedMapTypings[T] {
return obj[key];
}
let str = foo('str'); // correctly inferred type 'string'
不过,如果我使用推断接口类型作为参数,它不工作:
function fn<T extends keyof AllowedMapTypings>(key: string, kind: T, value: AllowedMapTypings[T]) {
if (kind === 'str') {
console.log(value.length); // Property 'length' does not exist on type 'AllowedMapTypings[T]'.
}
}
它看起来像qazxsw POI不能正确完成其作为一种警卫的工作状况。我失去了一些东西,或者这是TS缺少的特性/错误?
这是一个kind === 'str'
。那种控制流变窄,当你做known limitation不会对一般类型参数或这种类型的值行为发生这种情况。我想,人们可以说,if (kind === 'str') {}
应该从if (kind === 'str') {}
缩小kind
的类型T
,但即使编译器这样做,你就不会缩小T & 'str'
的类型。即使你知道value
的类型与kind
的相关性,编译器不。
你总是可以绕过这与value
的自由主义用途。如果你想多一点的类型安全,你可以使用相关的值成可以缩小你期望的方式单个变量的解决方法是扩大泛型类型混凝土工会的价值观,和包。例如:
type assertions
该类型type KindValuePair<K extends keyof AllowedMapTypings = keyof AllowedMapTypings> =
K extends any ? [K, AllowedMapTypings[K]] : never;
扩展到KindValuePair
,这是你真正想要让尽可能["str", string] | ["lon", number] | ["str2", string]
和kind
事工会。 (我可能只是手动设置value
到工会,而是我使用KindValuePair
可以让编译器数字出来我。)
然后,你可以这样做:
distributive conditional type
你断言function fn<T extends keyof AllowedMapTypings>(key: string, kind: T, value: AllowedMapTypings[T]) {
const kindValuePair = [kind, value] as KindValuePair; // assertion here
if (kindValuePair[0] === 'str') {
console.log(kindValuePair[1].length); // okay
}
}
的类型是[kind, value]
的,然后你可以使用控制流就缩小到KindValuePair
保持你期望它的元素之间就看你核对kindValuePair
后的关系。如果这对你的作品,你甚至可以使功能的具体使用'str'
不是通用的:
rest parameters
这完全避免了断言和是类型安全的,因为我可以想像决策。它也有取缔这样调用的副作用:
function fn(key: string, ...kindValuePair: KindValuePair) {
if (kindValuePair[0] === 'str') {
console.log(kindValuePair[1].length); // okay
}
}
被允许在通用版本(它指定fn("", Math.random() < 0.5 ? 'str' : 'lon', 1); // error
为T
)。
不管怎样,希望给你一些想法。祝好运!