我面临着一个打字泛型的问题.

我有一个函数,它必须通过一个类型和一些依赖于该类型的属性来调用. 在IF中将类型设为TYPES.ME时,TypeScrip无法推断属性的类型:

enum TYPES {
    ME = 'me',
    YOU = 'you'
}

type Attributes<T extends TYPES> =  T extends TYPES.ME ? {keys: true} : {hat: true}
const func = <T extends TYPES>(type: T, attr: Attributes<T>) => {
    if(type === TYPES.ME) {
        attr.keys // error Property 'keys' does not exist on type '{ keys: true; } | { hat: true; }'
    }
} 

Playground link

我想听听你对这件事的意见,看看有没有好的变通办法.

干杯!

推荐答案

目前,作为control flow analysis的结果,TypeScrip无法重新约束generic个类型参数.在func(type, attr)的身体里,你判断type === Types.ME.这可以将type的类型从T变为类似T & Types.ME的类型.But it cannot do anything to 106 itself.类型参数T顽固地保持不变;它不被限制为Types.ME.因此编译器不能得出attr是类型Attributes<Types.ME>的结论.

编译器拒绝更改T在技术上是正确的.这是因为,虽然像type这样的单个值一次只能是一件事,但像T这样的类型参数可以是union.事实上,您可以拨打func(),T等于完整的Types.ME | Types.YOU联盟,如下所示:

func(
    Math.random() < 0.999 ? Types.ME : Types.YOU,
    { hat: true }
) // compiles without error

如果判断一下,您会发现T被推断为Types(Types.ME | Types.YOU的完全并集,因此,即使在typeTypes.ME的99.9%的可能性的情况下,attr也被允许为{hat: true}.

microsoft/TypeScript#27808有一个长期开放的功能请求,它要求一种方式来表示"T将恰好是Types.METypes.YOU中的一个;它不能是一个联合".然后,也许在函数体内部,判断type === Types.ME将允许T本身被约束为Types.ME,并且事情将按预期工作.而且,拨打Math.random() < 0.999的电话大概会被拒绝.

但现在,它不是语言的一部分.


您可以考虑采用这样的方法,不是让func成为泛型,而是使其类似于overloaded函数,其中Types的每个成员都有一个调用签名.您可以将其编写为具有rest parameter的函数,其类型为tuple typesdiscriminated union,编译器将为treat it as such inside the function body.

也许是这样的:

type FuncArg =
    [type: Types.ME, attr: { keys: true }] |
    [type: Types.YOU, attr: { hat: true }];

const func: (...args: FuncArg) => void = (type, attr) => {
    if (type === Types.ME) {
        console.log(attr.keys) // this works
    }
}

现在,您不能进行无效调用:

func(
    Math.random() < 0.999 ? Types.ME : Types.YOU,
    { hat: true }
) // error, type 'Types.ME' is not assignable to type 'Types.YOU'

一切都是可行的,因为现在在FuncArg中,typeattr被Bundle 在一起;这就像想象T被约束为一次只是Types的一个成员,并遍历各种可能性.请注意,如果需要,可以从问题中给出的Attributes类型或从另一个映射接口计算FuncArg,但这超出了问题的范围.

Playground link to code

Typescript相关问答推荐

React Hook中基于动态收件箱定义和断言回报类型

如何从TypScript中的接口中正确获取特定键类型的所有属性?

如何防止TypeScript允许重新分配NTFS?

TS不推断类型在条件判断后具有属性'

如何用不同的键值初始化角react 形式

为什么typescript要把这个空合并转换成三元?

类型脚本接口索引签名

打印脚本丢失函数的泛型上下文

具有动态键的泛型类型

Angular 信号:当输入信号改变值时,S触发取数的正确方式是什么?

有没有办法解决这个问题,类型和IntrinsicAttributes类型没有相同的属性?

从TypeScrip中的其他类型检索泛型类型

Typescript 中相同类型的不同结果

类型与泛型抽象类中的类型不可比较

TypeScrip-根据一个参数值验证另一个参数值

如何连接属性名称和值?

在抽象类属性定义中扩展泛型类型

在构建Angular 应用程序时,有没有办法通过CLI传递参数?

在Google授权后由客户端接收令牌

为什么看起来相似的代码在打字时会产生不同的错误?