Dariusz Męciński
Dariusz Męciński

Reputation: 31

Typescript How To handle Type or Array of Types

express-fileupload has declaration like this:

declare namespace fileUpload {
class FileArray {
    [index: string]: UploadedFile | UploadedFile[]
}

interface UploadedFile {
    name: string;
    encoding: string;
    mimetype: string;
    data: Buffer;
    truncated: boolean;
    mv(path: string, callback: (err: any) => void): void;
    mv(path: string): Promise<void>;
}

Then in controller Iam using it as:

const file: UploadedFile | UploadedFile[] = req.files.file;

But now TypeScript monits

Property 'name' does not exist on type 'UploadedFile | UploadedFile[]'. 
Property 'name' does not exist on type 'UploadedFile[]'.

for file.name

Bacause In Array Type theres no poperty "name".

How tyo handle this situtuation ? A try

if (file instanceof Array)
if (file typeof UploadedFile[])

But this not works.

Upvotes: 0

Views: 1238

Answers (1)

JJWesterkamp
JJWesterkamp

Reputation: 7916

The if (file typeof UploadedFile[]) part will not work because typeof is a JS runtime check, and UploadedFile[] is a type. The concept of types does not really exist at runtime, therefore it's impossible to execute the statement.

But the first check for file being an array should actually be enough. By writing code that handles the type UploadedFile | UploadedFile[] you're basically trusting any variable that is assigned that type to have a value of that type at runtime. So if it's not an array, it must be a value of type UploadedFile:

if (Array.isArray(req.files.file)) {
    // It must be an array of UploadedFile objects...
} else {
    // It must be a single UploadedFile object...
}

The tedious part is that as long as a variable has a union type like this, you'd have to write constructs similar to if...else anywhere you want to perform operations on the variable (read it, transform it, or otherwise use it). Is there a cleaner way?

You could normalize the value of file to always be an array, and then always treat it as such

If req.files.file is an array, let myFiles be req.files.file. Otherwise let myFiles be an array containing 1 element which is req.files.file:

const myFiles: UploadedFile[] = Array.isArray(req.files.file)
    ? req.files.file
    : [req.files.file];

Now consider having a function handleSingleFile that you must run for all files that came out of req.files.file. Instead of writing:

if (Array.isArray(req.files.file)) {
    req.files.file.forEach((file) => handleSingleFile(file));
} else {
    handleSingleFile(req.files.file);
}

...you know that myFiles will always be an array:

myFiles.forEach((file) => handleSingleFile(file));

Upvotes: 2

Related Questions