typescript - 如何避免 TypeScript 区分联合中的双重不可为空检查?
问题描述
在下面的代码中,TypeScript 不相信 if customMessage
is undefined
,className
is NOT undefined
:
export default class ClassRequiredInitializationHasNotBeenExecutedError extends Error {
public static readonly NAME: string = "ClassRequiredInitializationHasNotBeenExecutedError";
public constructor(
parametersObject: {
customMessage: string;
className?: undefined;
} | {
className: string;
customMessage?: undefined;
}
) {
super();
this.name = ClassRequiredInitializationHasNotBeenExecutedError.NAME;
if (typeof parametersObject.customMessage !== "undefined") {
this.message = parametersObject.customMessage;
} else {
this.message = ClassRequiredInitializationHasNotBeenExecutedError.buildMessage({
className: parametersObject.className
})
}
}
private static buildMessage(parametersObject: { className: string }): string {
return `The class '${parametersObject.className}' has not been executed;`
}
}
错误:
Type 'string | undefined' is not assignable to type 'string'.
Type 'undefined' is not assignable to type 'string'.(2322)
我知道如果会进行仔细检查,例如:
if (typeof parametersObject.customMessage !== "undefined") {
this.message = parametersObject.customMessage;
} else if (typeof parametersObject.className !== "undefined") {
this.message = ClassRequiredInitializationHasNotBeenExecutedError.buildMessage({
className: parametersObject.className
})
上面的例子可以工作,但是:
else if (typeof parametersObject.className !== "undefined")
如果可能,我想避免- 事件离开它,TypeScript 不会相信其中之一
customMessage
或className
已被初始化。这是关键时的示例:
let message: string;
if (typeof parametersObject.customMessage !== "undefined") {
message = parametersObject.customMessage;
} else if (typeof parametersObject.className !== "undefined") {
message = ClassRequiredInitializationHasNotBeenExecutedError.buildMessage({
className: parametersObject.className
})
}
console.log(message.length)
Variable 'message' is used before being assigned.(2454)
请注意,这个问题是关于如何让 TypeScript 相信 if customMessage
is undefined
, className
is NOT undefined ,反之亦然,而不是如何初始化this.message
(ClassRequiredInitializationHasNotBeenExecutedError
该类只是示例)
解决方案
parametersObject: {
customMessage: string;
className?: undefined;
} | {
className: string;
customMessage?: undefined;
}
可以简化为:
parametersObject: {
customMessage: string;
} | {
className: string;
}
因为property?: undefined
几乎没有额外的信息。
那么条件可以表示为:
if ('customMessage' in parametersObject) {
this.message = parametersObject.customMessage;
} else {
this.message = ClassRequiredInitializationHasNotBeenExecutedError.buildMessage({
className: parametersObject.className
})
}
更新
严格强制一个或另一个选项:
type Params<T extends {
customMessage: string;
} | {
className: string;
}> = T extends {
customMessage: any;
className: any;
} ? never : T
export default class ClassRequiredInitializationHasNotBeenExecutedError<P extends {
customMessage: string;
} | {
className: string;
}> extends Error {
public static readonly NAME: string = "ClassRequiredInitializationHasNotBeenExecutedError";
public constructor(
parametersObject: P extends Params<P> ? P : never
) {
super();
this.name = ClassRequiredInitializationHasNotBeenExecutedError.NAME;
if ('customMessage' in parametersObject) {
this.message = parametersObject.customMessage;
} else {
this.message = ClassRequiredInitializationHasNotBeenExecutedError.buildMessage({
className: parametersObject.className
})
}
}
private static buildMessage(parametersObject: { className: string }): string {
return `The class '${parametersObject.className}' has not been executed;`
}
}
new ClassRequiredInitializationHasNotBeenExecutedError({ customMessage: '' }) // ok
new ClassRequiredInitializationHasNotBeenExecutedError({ className: '' }) // ok
new ClassRequiredInitializationHasNotBeenExecutedError({ customMessage: '', className: '' }) // error as expected
推荐阅读
- python-2.7 - AWS Schedule Batch-Job 通过 Boto3 SDK 传递一些环境变量
- javascript - Wallpaper Engine,关于自定义 Audio Visualizer 渲染的见解
- python - 如何将偶数行的数据附加到上面的奇数行?
- android - android 项目在 Linux 上构建,但不在 Windows 上
- spring-boot - 我们可以在tomcat服务器中部署spring-boot jar吗?
- php - 使用 array_splice 以外的其他方法组合两个数组
- python - Google OAuth 不会在 authlib.starlette_client 中返回 refresh_token
- swift - 错误的文件名快速保存在文档目录中
- javascript - 使用 css 属性将文本换行在两行之后
- java - 关闭舞台后,儿童舞台听众不会被杀死