I want to write a function to retrive the file content from source file. The source file has only two kinds of structure. I want to use discriminated unions with user-defined type guard to handle the content.
I need exhaustiveness checking so I turned on the strictNullChecks
. I expect that if it creates discriminated unions succesfully, the compiler will not add | undefined
| undefined
to the returned fileContent
in function retrieveFileContent
. But the Typescript compiler throw an error:
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'
How can I modify my code to do discriminated unions with user-defined type guard right? Or is there any other better solution for my use case?
Sorce Code:
// 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
}
My 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/**/*"
]
}
As far as I know, that type of exhaustiveness checking only works with 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;
}
And the check you're doing is not really suited to a switch
statement. Luckily, you could use the second method of exhaustiveness checking listed in the docs : using a helper function which throws a compiler error if file
is not narrowed to never
after all the checks have completed:
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
}
Hope that helps. Good luck!
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.