【问题标题】:`keyof` reported compile error when using generic type`keyof` 使用泛型时报编译错误
【发布时间】:2019-04-23 13:12:19
【问题描述】:

首先我有一个类型定义,使用keyof将所有属性映射到数字

type Numeric<T> = {
    [K in keyof T]: number
}

下面是我将使用的一个类。

class Entity {
    aNumber: number;
}

下面是一个接受泛型类型参数和类型为Numberic&lt;T&gt; 的局部变量的函数。但是当我分配{ aNumber: 1 } 时,它给出了编译错误。

const fn = <T extends Entity>() => {
    const n: Numeric<T> = {
//        ^
//        Type '{ aNumber: number; }' is not 
//        assignable to type 'Numeric<T>'
        aNumber: 1
    };
};

我不知道为什么不能将{ aNumber: number; } 分配给Numeric&lt;T&gt;,因为类型参数T 必须从Entity 扩展而来,并且它必须包含一个名为aNumber 的键。这意味着aNumber 必须是类型 T 的键并且应该能够分配给Numeric&lt;T&gt;。

【问题讨论】:

    标签: typescript


    【解决方案1】:

    错误信息具有误导性。但是有一个错误,这就是 TypeScript 正在捕获的。确实Entity没有错:

    type Numeric<T> = {
        [K in keyof T]: number
    }
    interface Entity {
        aNumber: number
    }
    
    // No error
    const n: Numeric<Entity> = {
        aNumber: 1
    };
    

    但是,当您说 T extends Entity 时,它会为非数字值打开它,例如

    type Numeric<T> = {
        [K in keyof T]: number
    }
    interface Entity {
        aNumber: number
    }
    
    // No error
    const n: Numeric<Entity> = {
        aNumber: 1
    };
    
    interface X extends Entity {
        notANumber: string
    }
    // Error. Thank you TypeScript
    const o: Numeric<X> = {
        aNumber: 1
    };
    

    因此,当您在 Numeric 中使用 T extends Entity 时会出现错误。

    【讨论】:

    • 不,我不认为你的解释是正确的。我确实在 TS 部分看到了一个错误,但在你的情况下没有。检查我的答案。
    【解决方案2】:

    &lt;T extends Entity&gt; 施加的约束应被视为T 应满足的最低要求。这意味着“T 至少应该包含aNumber: number 对”。

    让我们看看const n: T。这意味着“n 至少应该包含T 中的任何键值对”。

    现在我们知道,T 有一对 aNumber: number,但请记住,这只是最低要求。 T 也可能是 { aNumber: number; aString: string }。这就是为什么这也会给你错误。

    // if you understand this:
    const n: { aNumber: number; aString: string } = { aNumber: 42 }  // error, of course
    // you see why this is an error:
    const n: T = { aNumber: 42 }  // also error
    

    你永远无法知道T 到底是什么。 keyof T 很好,因为我们至少知道 T 的密钥之一。

    const k: keyof T = "aNumber"
    

    为了证明我的观点,让我们回顾一个非通用案例。以@basarat 的代码为例,这里X 不是流派。

    type Numeric<T> = {
        [K in keyof T]: number
    }
    
    type OptionalNumeric<T> = {
        [K in keyof T]?: number
    }
    
    interface Entity {
        aNumber: number
    }
    
    interface X extends Entity {
        notANumber: string
    }
    
    // Error, because `notANumber` is missing
    const o: Numeric<X> = { aNumber: 1 };
    // Correct
    const o1: Numeric<X> = { aNumber: 1, notANumber: 2 };
    // Also correct, because all keys are optional.
    const o2: OptionalNumeric<X> = { aNumber: 1 };
    

    旁注。以上应该解释你的情况。但是,我确实认为 TS 中存在错误。

    我认为如果您在原始情况下使用OptionalNumeric,您想要的应该可以工作。但事实证明并非如此。涉及泛型类型参数时应该是缺陷。

    【讨论】:

    • 即使我使用了type Numeric&lt;T&gt; = { [K in keyof T]?: number },错误仍然存​​在。但是如果我在Numeric&lt;T&gt; 中传递一个类型参数,它会很好地工作,例如const n: Numeric&lt;Entity&gt;。但我使用的是通用参数,就像我在示例 const fn = &lt;T extends Entity&gt;() =&gt; { const n: Numeric&lt;T&gt; = { aNumber: 1 }; }; 中所做的那样,它失败了。
    • @ShaunXu 我说的是a)你试图做的事情是错误的; b) 可选键应该可以工作,但它没有并且应该被视为一个错误。我只有解释,没有解决方案。
    • 这里的解决方法是(假设我们使用OptionalNumeric)将n 分配给OptionalNumeric&lt;T &amp; Entity&gt; 类型而不是OptionalNumeric&lt;T&gt;。不过,我不知道OptionalNumeric&lt;T&gt; 的问题是否已知。
    • 我以为这与Microsoft/TypeScript#13442 有关,但我想不是。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-04-21
    • 1970-01-01
    • 2014-07-25
    • 2020-03-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多