我有一个简单的对象
const trans = {
a: {
valueA: 'a'
},
b: {
valueB: 'b'
}
};
当我想获取trans.a.valueA的动态值时,我可以这样做:
console.log(trans['a'].valueA);
但是当我尝试将其包装在这样的函数中时:
function foo(key:keyof typeof trans) {
return trans[key];
}
并尝试获取值“ valueA”
console.log(foo('a').valueA);
我遇到TS错误
Property 'valueA' does not exist on type '{ valueB: string; }'
那么我如何从函数中获得动态值呢? 干杯
答案 0 :(得分:1)
问题在于keyof typeof trans
等于union "a" | "b"
。
所有编译器对foo()
的了解是,它采用类型为"a" | "b"
的值,并以此返回索引到trans
的类型。
因此,foo()
的输出类型将为(typeof trans)["a" | "b"])
与(typeof trans)["a"] | (typeof trans)["b"]
或
{ valueA: string; } | { valueB: string; }
,它本身就是一个联合体。
如果我给您一个该类型的值,您将无法仅获得其valueA
属性,而不能确保它具有第一个属性。那是您得到的警告...编译器告诉您foo()
可能的输出为{ valueB: string; }
,它没有valueA
属性。
因此您可以自己检查一下以使编译器满意:
const fooA = foo("a");
if ("valueA" in fooA) {
// fooA is now known to be {valueA: string}
console.log(fooA.valueA) // okay now
} else {
// fooA is now known to be {valueB: string}
throw new Error("THE WORLD MAKES NO SENSE");
}
好,现在一切都很好,我们都摆在这里了,对吧?开玩笑吧。
如果您将"a"
传递到foo()
中,则输出可能必须是{{ 1}}。没错,但是编译器不会自动尝试验证这一点。获得所需行为的方法是使用generic函数:
{valueA: string}
在这种情况下,function foo<K extends keyof typeof trans>(key: K) {
return trans[key];
}
是泛型类型,必须是K
的子类型。实际上,这意味着"a" | "b"
可以是K
类型,也可以是"a"
类型,也可以是完整联合"b"
。 "a" | "b"
的返回类型现在为foo()
,即lookup type。
现在调用(typeof trans)[K]
时,编译器将尝试为foo()
推断 最窄类型,并返回类型结果将是最窄的类型。
K
对于// K inferred as "a":
const fooA = foo("a");
// fooA is of type (typeof trans)["a"], which is {valueA: string}
console.log(fooA.valueA); // okay
,
"b"
并且,为了证明检查联合类型的合法需求:
// K inferred as "b":
const fooB = foo("b");
// fooB is of type (typeof trans)["b"], which is {valueB: string}
console.log(fooB.valueB); // okay
he。好的,希望对您有所帮助。祝你好运!
答案 1 :(得分:0)
最简单的方法:
function foo(key:keyof typeof trans) {
return trans[key] as any;
}