使用TS条件类型来缩小数值属性

时间:2019-05-13 20:50:57

标签: typescript

我正在研究条件类型,并试图对此进行验证。有趣的是,如果我通过“ val”作为参数,则test()函数会执行 验证,如果我通过“ name”,则验证失败(这是预期的行为)。但是,Typescript显然不认为o [p]可以依赖为数字,而是抛出以下内容:

运算符'+ ='不能应用于类型'number'和'T [{[T键中的K]:T [K]扩展数字? K:从不; } [T键]]。

我是否以某种方式误解了用法?我以为“从不”会禁止任何不是数字的参数。

class Test {
    public static SumParam<T>
        (t: T[], p:{ [K in keyof T]: T[K] extends number ? K : never }[keyof T]): number {
        let n:number = 0;
        for (let o of t) {
            n += o[p]; //Operator '+=' cannot be applied to types 'number' and 'T[{ [K in keyof T]: T[K] extends number ? K : never; }[keyof T]]'.
        }
        return (n);
    }
    public test(): void {
        Test.SumParam(
            [{ name: "alice", val: 3 },
            { name: "bob", val: 4 }],
            "val"); //validates
        Test.SumParam(
            [{ name: "alice", val: 3 },
            { name: "bob", val: 4 }],
            "name"); //Argument of type '"name"' is not assignable to parameter of type '"val"'.
    }
}

编译器缩小范围并认识到“ val”是唯一可枚举的属性扩展数字的事实……这并不意味着条件语法正在起作用……?

2 个答案:

答案 0 :(得分:2)

Typescript将无法遵循仍具有未解析的类型参数的条件类型。因此,它将无法知道o[p]的类型为数字。

如果您不介意在数组中的项目上出现错误,则可以通过允许打字稿知道o[p]是数字的方式来键入函数

class Test {
    public static SumParam<T extends Record<K, number>, K extends keyof T>
        (t: T[], p: K): number {
        let n:number = 0;
        if (!t) return (n);
        for (let o of t) {
            n += o[p]; //ok.
        }
        return (n);
    }
    public test(): void {
        Test.SumParam(
            [{ name: "alice", val: 3 },
            { name: "bob", val: 4 }],
            "val"); //ok, no error here
        Test.SumParam(
            [{ name: "alice", val: 3 }, // Type 'string' is not assignable to type 'number'.
            { name: "bob", val: 4 }], // Type 'string' is not assignable to type 'number'.
            "name"); 
    }
} 

答案 1 :(得分:0)

我为name遇到了其他错误,但总的来说,它可以正常工作:

Playground

class Test {
  public static SumParam<K extends string, T extends { [key in K]: number }>(t: T[], p: K): number {
        let n:number = 0;
        for (let o of t) {
            n += o[p];
        }
        return (n);
    }
    public test(): void {
        Test.SumParam(
            [{ name: "alice", val: 3 },
            { name: "bob", val: 4 }],
            "val"); //validates
        Test.SumParam(
            [{ name: "alice", val: 3 },
            { name: "bob", val: 4 }],
            "name"); // Type 'string' is not assignable to type 'number'.
    }
}