【问题标题】:How can I hint to the Typescript compiler to infer string literal types for properties?如何提示 Typescript 编译器推断属性的字符串文字类型?
【发布时间】:2017-12-02 08:56:33
【问题描述】:

Typescript 编译器将为 consts 推断字符串文字类型:

const a = 'abc';
const b: 'abc' = a; // okay, a is of type 'abc' rather than string

但是,对于属性,类型被推断为string

const x = {
    y: 'def',
};

const z: { y: 'def' } = x; // error because x.y is of type string

在此示例中,如何让编译器推断 x 的类型为 { y: 'def' } 而无需为 x 编写类型注释?

编辑:有一个开放的issue 请求支持此功能。一种建议的解决方法是使用如下语法:

const x = new class {
    readonly y: 'def';
};

const z: { readonly y: 'def' } = x; // Works

在 Playground 中尝试here

编辑 2: 甚至还有一个开放的PR 可以解决这个问题。禁用类型扩展似乎是一个流行的请求。

【问题讨论】:

  • 你可以用type assertion告诉编译器某些东西有特定的类型:const x = { y: 'def' as 'def', };

标签: typescript


【解决方案1】:

我认为您正在寻找在 TS 3.4 中添加的 const assertion

您只需将as const 添加到字符串中即可使其成为文字类型。

const x = {
    y: 'def' as const,
};

const z: { y: 'def' } = x; // no error :)

TS playground link

【讨论】:

    【解决方案2】:

    不同之处在于属性没有const 关键字。由于无法确保属性不会发生变异,TS 不能假定一个常量字符串文字,它必须假定更通用的 string

    尝试用let 替换您示例中的第一个const,并且在该位置TS 也将假定string 而不是'abc'

    let a = 'abc';
    const b: 'abc' = a; 
    

    TS Playground link for this code

    将显示b 的错误“类型字符串不可分配给类型'abc'”。

    由于 TS 无法从语言功能推断出不变性,就像您在 const 变量示例中所做的那样,唯一的方法是通过显式类型注释告诉它 obejct 属性是不可变的,这意味着您的问题的答案是否定的。

    【讨论】:

      【解决方案3】:

      是的,这个问题 (Microsoft/TypeScript#10195) 对于喜欢保留它的人来说很烦人DRY。正如@artem 提到的,您可以这样做:

      const x = {
          y: 'def' as 'def'  // WET  
      };
      const z: { y: 'def' } = x; // okay
      

      但这需要你提到两次'def';一次作为值,一次作为类型。 TypeScript 可以被强制为泛型类或函数中的类型参数推断更窄的类型,但不能在对象字面量内推断。


      但是,如果您愿意使用自定义库和更多开销,您可以这样做:

      const x = LitObj.of('y', 'def').build(); // DRY
      const z: { y: 'def' } = x; // okay
      

      LitObj 的定义是这样的(最好是在它自己的模块中,远离你的代码):

      type Lit = string | number | boolean | undefined | null | {};
      class LitObj<T> {
        obj = {} as T;
        private constructor() {
        }
        and<K extends string, V extends Lit>(k: K, v: V): LitObj<T & Record<K, V>> {
          var that = this as any;
          that.obj[k] = v;
          return that;
        }
        build(): {[K in keyof T]: T[K]} {
          return this.obj;
        }
        static of<K extends string, V extends Lit>(k: K, v: V): LitObj<Record<K,V>> {
          return new LitObj<{}>().and(k,v);
        }
      }
      

      这个想法是LitObj 是字面类型对象的构建器。在运行时它只是向对象添加属性,但定义允许 TypeScript 跟踪文字键和值类型。无论如何,希望这会有所帮助。祝你好运!

      【讨论】:

        猜你喜欢
        • 2017-08-24
        • 2019-07-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-12-22
        • 1970-01-01
        • 1970-01-01
        • 2019-08-17
        相关资源
        最近更新 更多