MattMan569
MattMan569

Reputation: 105

Returning a single type from a union return type determined by a parameter value

I have the following Typescript code:

type Encoding = 'binary' | 'utf8';
type EncodingType = { binary: string, utf8: Uint8Array };

interface FS {
  readFile: <T extends Encoding>(path: string, opts?: { encoding?: T, flags?: string }) => EncodingType[T];
}

type FSMethodNames = { [K in keyof FS]: (FS)[K] extends (...args: any[]) => any ? K : never }[keyof FS];
type FSMethodArgs = { [K in FSMethodNames]: Parameters<FS[K]> };
type FSMethodReturn = { [K in FSMethodNames]: ReturnType<FS[K]> };

FS<Method extends FSMethodNames>(method: Method, ...args: FSMethodArgs[Method]): FSMethodReturn[Method];

I am attempting to return a single type from the readFile function depending on the provided value for encoding, which is either string or utf8. As of now it is always returning string | Uint8Array.

This is in a typescript index.d.ts to add typings to preexisting JS code.

Example call:

import { createFFmpeg, fetchFile } from '@ffmpeg/ffmpeg';

const ffmpeg = createFFmpeg();
const video: File;

await ffmpeg.load();

// Get video from user...

// Write the file 
ffmpeg.FS('writeFile', 'video', fetchFile(video));

// Misc. FFmpeg code to convert to gif...

// Read the result
// All three possible desired results shown
const dataA = ffmpeg.FS('readFile', 'out.gif'); // Should be string
const dataS = ffmpeg.FS('readFile', 'out.gif', { encoding: 'binary' }); // Should be string
const dataU = ffmpeg.FS('readFile', 'out.gif', { encoding: 'utf8'); // Should be Uint8Array

The current code works, but dataA, dataS, and dataU are all of string | Uint8Array type.

Upvotes: 0

Views: 95

Answers (1)

Karol Majewski
Karol Majewski

Reputation: 25790

What you tried won't work because you need two generics, not one. FS would have to be generic over the method and over the encoding, but there is only one generic defined.

If you can accept a curried API, then you can use this instead:

declare function FS<Method extends FSMethodNames>(method: Method): FS[Method];

const string = FS('readFile')('index.js', { encoding: 'binary' });
const uintarray = FS('readFile')('index.js', { encoding: 'utf8' });
const either = FS('readFile')('index.js');

Playground

Upvotes: 1

Related Questions