Typescript 3.5中的索引访问

Typescript 3.5中的索引访问,typescript,Typescript,我想写一个类型安全的getter,它也可以返回一些修改过的值(但我认为仍然是正确的) class-Foo{ a:数字; b:布尔型; } 函数getProp(o:Foo,p:K):Foo[K]{ 开关(p){ 案例“a”: 返回1; 案例“b”: 返回o[p]; } 返回未定义; } 在上面的代码中,我希望getProp返回Foo[K],这样在const v=getProp(f,'a')v类型为number。但是,代码抛出一个错误,我返回1,因为它不能被分配到从不 请注意,这在Typescri

我想写一个类型安全的getter,它也可以返回一些修改过的值(但我认为仍然是正确的)

class-Foo{
a:数字;
b:布尔型;
}
函数getProp(o:Foo,p:K):Foo[K]{
开关(p){
案例“a”:
返回1;
案例“b”:
返回o[p];
}
返回未定义;
}
在上面的代码中,我希望
getProp
返回
Foo[K]
,这样在
const v=getProp(f,'a')
v
类型为
number
。但是,代码抛出一个错误,我返回
1
,因为它不能被分配到
从不

请注意,这在Typescript 3.4中起作用,因为“修复了对索引访问类型的错误写入”()


如何最好地编写此代码,但不编写
返回1作为任何

不要认为有一种方法可以用一种类型安全的方式来写这篇文章(但是如果有人有天赋的话,我非常愿意删除这个答案)。因为p是
K
类型,所以它永远不会缩小,因为它是一个并集(即使它扩展了一个并集)。此外,您不能为
Foo[K]
指定具体值,因为它仍然包含未解析的泛型参数
K

实现这一点的一种方法是使用单独的实现签名,这比任何
都要好一点:


class Foo {
    a!: number;
    b!: boolean;
}

function getProp<K extends keyof Foo>(o: Foo, p: K): Foo[K] | undefined
function getProp(o: Foo, p: keyof Foo): Foo[keyof Foo] | undefined {
    switch (p) {
        case 'a':
            return 1;
        case 'b':
            return o[p];
    }
    return undefined;
}


福班{
a!:数字;
b!:布尔;
}
函数getProp(o:Foo,p:K):Foo[K]|未定义
函数getProp(o:Foo,p:keyof-Foo):Foo[keyof-Foo]|未定义{
开关(p){
案例“a”:
返回1;
案例“b”:
返回o[p];
}
返回未定义;
}
是的,自从TypeScript 3.5发布以来,这个问题经常出现。正如您所注意到的,这是一个捕获了大量实际bug的方法,但不幸的是,它也会对您正在执行的相当安全的代码发出警告

在这一点上,潜在的问题是扩展联合的泛型类型参数不会通过控制流分析缩小范围(请参阅)。编译器没有办法说既然
p
可以缩小到
“a”
,那么类型参数
K
应该缩小到
“a”
。通常不允许这样做,因为
K
可能总是
“a”|“b”
。该语言目前缺少一些能够安全实现这一点的功能(例如,如果
K
可以说不是
扩展了“a”|“b”
,而是类似于
“a”|“b”
,那么它必须是
“a”
“b”
,而不是
“a”|“b”
,这将有所帮助)


目前还有变通办法和重构。这是一种可能的工作方式,使用一个调用签名重载函数将实现内部的行为恢复到TS3.4及以下版本中完成的检查。是的,它不安全;这就是TS3.5所修复的。涉及类型断言或其他不合理行为的其他变通方法也应该有效

我想添加这个答案(除了提供上面的链接)的唯一原因是提到一个重构,它可能会有所帮助,这取决于您的用例。这样做的目的是完全忘记控制流变窄,因为编译器不能对泛型执行此操作。相反,您应该返回编译器可以识别为合法的
Foo[K]
,方法是使用
K
类型的键索引到
Foo
类型的对象。毕竟,如果你眯着眼睛看一个对象,索引到它就像在键上做一个
开关

function getProp<K extends keyof Foo>(o: Foo, p: K): Foo[K] {
  return { a: 1, b: o.b }[p];
}
函数getProp(o:Foo,p:K):Foo[K]{ 返回{a:1,b:o.b}[p]; }

这是一个非常有趣的解决方案。不幸的是,它仍然允许
返回true即使它不是类型
number
(与
a
所预期的一样)。
function getProp<K extends keyof Foo>(o: Foo, p: K): Foo[K] {
  return { a: 1, b: o.b }[p];
}