来自对象TypeScript的动态值

时间:2019-03-21 13:04:28

标签: typescript

我有一个简单的对象

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; }'

那么我如何从函数中获得动态值呢? 干杯

2 个答案:

答案 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;
  }