I am trying to implement a variadiac argument for a generic function such that when the generic type is undefined, no argument is required, otherwise a single argument is matching the specified type.
For example
const a = variadiacFunc(); // ok
const b = variadicFunc<string>('A string') // ok
const c = variadicFunc<string>() // error Expected 1 arguments, but got 0; Arguments for the rest parameter 'context' were not provided.
const d = variadicFunc<string | undefined>() // ok
const e = variadicFunc<string | undefined>('Another string') // ok
I am implementing this using conditional tuple types
type ConditionalTupleArg<T> = [T] extends [undefined] ? [] : [T];
const variadicFunc = <T = undefined>(...context: ConditionalTupleArg<T>) {...};
Inspecting the types in TS playground, this seems to do exactly what I want
export type OptionalArgTuple<T> = [T] extends [undefined] ? [] : [T]
type UndefinedOnly = OptionalArgTuple<undefined>; // []
type StringOrUndefined = OptionalArgTuple<string | undefined>; // [string | undefined]
type StringOnly = OptionalArgTuple<string>; // [string]
In my local create-react-app project I am seeing an error because StringOrUndefined is resolving to [string]
rather than [string | undefined]
which is causing compile errors for invocation involving union with undefined
in the case of d
above.
I'm not sure why this is happening, if there are tsconfig options which control or influence this behavior which may be configured incorrectly in my local project.
The project is currently configured to use typescript 4.7.4, and my tsconfig.json is
{
"include": [
"src"
],
"exclude": [
"node_modules"
],
"compilerOptions": {
"target": "ES6",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"downlevelIteration": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"module": "CommonJS",
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"typeRoots": [
"./node_modules/@types",
"../../node_modules/@types",
"./src/types"
]
}
}
UPDATE: explicitly running tsc --v
locally suggests that the code is building with 4.5.4 and not 4.7.4 as is listed in my package.json, however, using v4.5.4 in TS playground the inferred types are still what I am expecting.
UPDATE 2: I set typescript.tsdk
in my vscode settings.json to point to the correct typescript version and the error displayed in the editor has now changed
variadicFunc<string | undefined>(); // Expected 1 arguments, but got 0; Arguments for the rest parameter 'ctx' were not provided.
Allowing the types to be distributed resolves the error:
export type ConditionalTupleArg<T> = T extends undefined ? [] : [T];
variadicFunc<string | undefined>(); // ok
variadicFunc<string | undefined>('a string'); // ok
So I think the issue my local build using an old (4.5.4) version of typescript.