我想编写一个从源文件检索文件内容的函数。源文件只有两种结构。我想将discriminated unions与user-defined type guard一起使用来处理内容。
我需要exhaustiveness checking,所以我打开了strictNullChecks
。我希望,如果它成功创建了有区别的联合,则编译器将不会将| undefined
添加到函数fileContent
中返回的retrieveFileContent
中。但是Typescript编译器会抛出错误:
error TS2322: Type '{ type: FileContentType; payload: string; } | { type: FileContentType; payload: { filename: string; path: string; }; } | undefined' is not assignable to type 'FileContent'.
Type 'undefined' is not assignable to type 'FileContent'
如何修改我的代码以使用用户定义的类型保护权进行区分联合?还是我的用例还有其他更好的解决方案?
代码:
// Target File Content Format
enum FileContentType {
disk,
memory,
}
interface FileContentInMemory {
type: FileContentType.memory
payload: string
}
interface FileContentInDisk {
type: FileContentType.disk
payload: {
filename: string
path: string
}
}
type FileContent = FileContentInMemory | FileContentInDisk
// Source File Containing Content
interface FileBasics {
fieldname: string
originalname: string
encoding: string
mimetype: string
size: number
}
interface FileInMemory extends FileBasics {
fileString: string
}
interface FileInDisk extends FileBasics {
filePath: string
}
type SourceFile = FileInMemory | FileInDisk
function isInMemory(file: SourceFile): file is FileInMemory {
return (<FileInMemory>file).fileString !== undefined
}
function isInDisk(file: SourceFile): file is FileInDisk {
return (<FileInDisk>file).filePath !== undefined
}
// Retrieve Content From File
function retrieveFileContent(file: SourceFile): FileContent {
let fileContent
if (isInMemory(file)) {
fileContent = {
type: FileContentType.memory,
payload: file.fileString
}
} else if (isInDisk(file)) {
fileContent = {
type: FileContentType.disk,
payload: {
filename: file.originalname,
path: file.filePath,
}
}
}
return fileContent
}
我的tsconfig.json
:
{
"compilerOptions": {
"allowJs": true,
"checkJs": false,
"esModuleInterop": true,
"module": "commonjs",
"moduleResolution": "node",
"noImplicitAny": true,
"outDir": "dist",
"sourceMap": true,
"strictNullChecks": true,
"rootDir": "src",
"jsx": "react",
"types": [
"node",
"jest"
],
"typeRoots": [
"node_modules/@types"
],
"target": "es2016",
"lib": [
"dom",
"es2016"
]
},
"include": [
"src/**/*"
]
}
答案 0 :(得分:0)
据我所知,这种穷举性检查仅适用于a switch
statement that is the last statement in a function:
type DiscriminatedUnion = {k: "Zero", a: string} | {k: "One", b: string};
// works
function switchVersion(d: DiscriminatedUnion): string {
switch (d.k) {
case "Zero": return d.a;
case "One": return d.b;
}
}
// broken
function ifVersion(d: DiscriminatedUnion): string {
// Error! might not return a string -----> ~~~~~~
if (d.k === "Zero") return d.a;
if (d.k === "One") return d.b;
}
您正在执行的检查实际上并不适合switch
语句。幸运的是,您可以使用the docs中列出的第二种穷举性检查方法:使用一个辅助函数,如果在完成所有检查后未将file
缩小到never
,则会引发编译器错误:
function assertNever(x: never): never {
throw new Error("Unexpected object: " + x);
}
function retrieveFileContent(file: SourceFile): FileContent {
let fileContent: FileContent; // use annotation to avoid any
if (isInMemory(file)) {
fileContent = {
type: FileContentType.memory,
payload: file.fileString
}
} else if (isInDisk(file)) {
fileContent = {
type: FileContentType.disk,
payload: {
filename: file.originalname,
path: file.filePath,
}
}
} else return assertNever(file); // no error, file is never
return fileContent; // no error, fileContent is FileContent
}
希望有帮助。祝你好运!