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。禁用类型扩展似乎是一个受欢迎的请求。
答案 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 :)