【问题标题】:Explicitly typing an object with TypeScript使用 TypeScript 显式键入对象
【发布时间】:2019-10-29 17:08:41
【问题描述】:

我正在将我的小库从 JavaScript 转换为 TypeScript,我在那里有一个函数

function create(declarations: Declarations) {

现在声明是一个对象,其键可以是两种类型:

  • 如果键是这些字符串之一:onMemeber / onCollection,那么值应该是一个数字
  • 对于任何其他字符串键,值应该是字符串

这可以通过 TypeScript 强制执行吗?我应该如何定义我的Declarations 接口?

【问题讨论】:

    标签: typescript


    【解决方案1】:

    TypeScript 中没有具体的类型可以代表您的 Declarations 形状。

    我将一般概念称为“默认属性”类型。 (要求此问题的 GitHub 问题是 microsoft/TypeScript#17867)您希望特定属性为一种类型,然后将任何其他属性“默认”为其他不兼容的类型。这就像一个index signature,没有所有属性都必须分配给它的约束。

    (为了清楚起见,不能使用索引签名:

    type BadDeclarations = {
        onMember: number, // error! number not assignable to string
        onCollection: number, // error! number not assignable to string
        [k: string]: string
    };
    

    索引签名[k: string]: string 意味着每个 属性都必须可分配给string,甚至是onMemberonCollection。要使索引签名真正起作用,您需要将属性类型从string 扩大到string | number,这可能对您不起作用。 )

    有一些 pull requests that would have made this possible,但看起来它们不会很快成为语言的一部分。

    通常在 TypeScript 中,如果没有可以工作的具体类型,您可以使用 generic type,它在某种程度上是 constrained。以下是我如何将Declarations 设为通用:

    type Declarations<T> = {
        [K in keyof T]: K extends 'onMember' | 'onCollection' ? number : string
    };
    

    这是create()L的签名

    function create<T extends Declarations<T>>(declarations: T) {
    }
    

    您可以看到declarations 参数的类型为T,它被限制为Declarations&lt;T&gt;。这种自引用约束确保对于declarations 中的每个属性K,其类型均为K extends 'onMember' | 'onCollection' ? number : stringconditional type 是您所需形状的相当直接的转换。

    让我们看看它是否有效:

    create({
        onCollection: 1,
        onMember: 2,
        randomOtherThing: "hey"
    }); // okay
    
    create({
        onCollection: "oops", // error, string is not assignable to number
        onMember: 2,
        otherKey: "hey",
        somethingBad: 123, // error! number is not assignable to string
    })
    

    我觉得这很合理。


    当然,使用泛型类型并非没有一些麻烦。突然之间,您想使用Declarations 的每个值或函数现在都需要是通用的。所以你不能做const foo: Declarations = {...}。你需要const foo: Declarations&lt;{onCollection: number, foo: string}&gt; = {onCollection: 1, foo: ""}。这太令人讨厌了,您可能希望使用辅助函数,例如允许为您推断此类类型,而不是手动注释:

    // helper function
    const asDeclarations = <T extends Declarations<T>>(d: T): Declarations<T> => d;
    
    const foo = asDeclarations({ onCollection: 1, foo: "a" });
    /* const foo: Declarations<{
        onCollection: number;
        foo: string;
    }>*/
    

    好的,希望对您有所帮助;祝你好运!

    Link to code

    【讨论】:

      【解决方案2】:

      这似乎是不可能的,in the TypeScript Deep Dive book 有一个部分就是关于这个的。虽然你可以声明一个类型,但实际上不能在 TS 中用它创建一个对象:

      // No error on this type declaration:
      type Declarations = {
          [key: string]: string;
      } & {
          onMember: number;
          onCollection: number;
      }
      
      // Error does appear here indicating type `number` is not assignable to type `string`.
      const declarations: Declarations = {
          onMember: 0,
          onCollection: 0,
          other: 'Is a string'
      }
      

      TypeScript Playground link.

      【讨论】:

        猜你喜欢
        • 2021-07-15
        • 1970-01-01
        • 1970-01-01
        • 2021-09-16
        • 2022-11-19
        • 2022-11-30
        • 2020-09-05
        • 1970-01-01
        • 2019-02-04
        相关资源
        最近更新 更多