假设我们有一个接口IFieldConfig
export interface IFieldInputConfig {
type: 'string' | 'password';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
default?: any;
}
export interface IFieldConfig extends IExtractConfig {
id: string;
form?: IFieldInputConfig;
}
并有一个使用该接口的对象
export const fieldCompany: IFieldConfig = {
id: 'company',
form: {
type: 'string',
default: 'Apple',
},
}
当我想使用它时,TypeScript认为fieldCompany.form.default
可能是未定义的,即使很明显它是已定义的。我了解,在声明之后,form
道具 可以删除,因此TypeScript没错。
如何告诉TypeScript这是密封的/不会被修改?
很明显,我可以创建更多特定的接口,但是我不想在每种可选道具的组合中都使用一个接口。我敢肯定有一种更简单,更直接的方法
答案 0 :(得分:4)
我可能会有点简单,但是跳出来的一个解决方案就是根本不说它是IFieldConfig
:
export const fieldCompany = {
id: 'company',
form: {
type: 'string' as const, // <=== Note
default: 'Apple',
},
};
现在,fieldCompany.form.default
的类型为string
,而不是string | undefined
。您需要在as const
上使用type: 'string'
,因为否则它将被推断为string
,而不是'string' | 'password'
。
该对象仍与IFieldConfig
兼容;可行:
const x: IFieldConfig = fieldCompany;
答案 1 :(得分:2)
通常来说,除非我想要使编译器“忘记”所分配值的详细信息,否则我不会为类型注释变量。只有联合类型的值会在赋值时变窄,并且IFieldConfig
本身不是联合类型。因此,当您将值分配给类型IFieldConfig
的变量时,不会通过控制流分析将变量的类型缩小到更具体的范围。有关更多信息,请参见microsoft/TypeScript#16976和microsoft/TypeScript#27706。
我在这里的建议(没有更多信息让我相信),不要使用fieldCompany
的注释,而是使用const
assertion来使推断的类型尽可能地窄:>
const fieldCompany = {
id: 'company',
form: {
type: 'string',
default: 'Apple',
},
} as const;
您可以根据需要进行调整(例如,可能只有type
的{{1}}是form
),具体取决于您打算允许更改的结构部分以及哪些部分应保持狭窄/固定状态。
由于TypeScript的类型系统是structural而不是标称类型,因此这不会阻止您将as const
用作fieldCompany
:
IFieldConfig
好的,希望能有所帮助;祝你好运!
答案 2 :(得分:0)
我在操场上尝试了您的代码,实际上它抱怨undefined
的可能性。
但是为了您的急救,您有一些解决方法!
fieldCompany.form!.default
您还可以禁用严格的空检查
tsconfig.json
{
"compilerOptions": {
"strictNullchecks": false
}
}
答案 3 :(得分:0)
您可以定义一个通用的DeepRequired来获得如下信息:
interface IExtractConfig{}
export interface IFieldInputConfig<L extends any = any> {
type: 'string' | 'password';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
default?: L;
}
export interface IFieldConfig<L extends any = any> extends IExtractConfig {
id: string;
form?: IFieldInputConfig<L>;
}
type DeepRequired<T> = Required<{
[K in keyof T]: Required<DeepRequired<T[K]>>
}>
export const fieldCompany: DeepRequired<IFieldConfig<string>> = {
id: 'company',
form: {
type: 'string',
default: 'Apple',
},
}
Typescript对default:any字段不满意。因此我将其更改为通用参数。
现在您拥有fieldCompany.form.default
有"string"
function user(fc:DeepRequired<IFieldConfig<string>>){
//here fc.form.default assumed to be "string" and not "string" | "undefined"
}
有时候,您必须具有带有可选属性的基本接口。但是从我的经验来看,最好使用所有属性作为强制属性。最近,您可以使用“局部”和“拾取”将原始形状切成碎片。