我如何提示Typescript编译器推断属性的字符串文字类型?

时间:2017-12-02 08:56:34

标签: typescript

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

在游乐场here尝试。

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

3 个答案:

答案 0 :(得分:3)

区别在于属性没有const关键字。由于没有办法确定属性不会被突变,因此TS不能假设一个常量字符串文字,它必须假定更通用的string

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

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

TS Playground link for this code

将显示b&#34的错误;类型字符串不能分配给' abc'"。

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

答案 1 :(得分:1)

是的,此问题(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跟踪文字键和值类型。无论如何,希望是有帮助的。祝你好运!

答案 2 :(得分:1)

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

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

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

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

TS playground link