I have a type that represents one of certain constants. Each type also has a specific callback function.
type MyType = 'A' | 'B' | 'C';
// Same for 'B', 'C' etc.
type callbackForA = (result: ResultForA) => void;
ResultForA
is a specific object for that belongs to A
.
I want to create a TypeScript type
or interface
(let's call it MagicType
) for an object which may have a key for each of these MyType
constants. The value for each of these keys can either be true
(or implicitly undefined
hence "may have") or a maybe a callback function with a specific call signature based on the type.
The following should be a valid MagicType
object:
const myObject: MagicType = {
'A': true | (result: ResultForA) => void,
'C': true;
}
where B
is not configured, and C
doesn't take a callback.
The following should be an invalid object for MagicType
.
const myObject: MagicType = {
'A': true | (result: ResultForB) => void;
'FOO': true
}
because A
has the wrong callback function (result: ResultForB) => void
and FOO
is not a valid option for MyType
.
How can you implement such a type?
What I tried, is extending the Result
type like this:
type CallbackFunctionVariadicAnyReturn = (...args: any[]) => any
interface MagicType extends Record<MyType, true | CallbackFunctionVariadicAnyReturn>;
Unfortunately, it neither detects superflous keys such as FOO
nor does it detect wrong callback functions.
Bonus thought / question:
Is there also a way to tightly couple the callbacks to each MyType
value? Maybe a tuple like this?
type MyTypePairs = ['A', (result: ResultForA) => void] | ['B', // ...
CodePudding user response:
type MagicType = {
'A': boolean | (result: ResultForA) => void,
'B': undefined,
'C': boolean,
}
What that does is adds type annotations for each of the three keys, and because TS won't allow any keys other than those that are listed, you can't do the 'FOO'
key, and the annotation on the callback means you can't do the (result: ResultForB) => void
callback on a.
CodePudding user response:
see TS Playground
// define your types
type ResultForA = { a: string };
interface ResultCollection {
A: ResultForA; // can use named type alias
B: { b: boolean }; // or simply inline your result types
C: { c: number };
}
// Bonus thought / question:
type ResultFunc<K extends keyof ResultCollection> = (result: ResultCollection[K]) => void;
// Tell object which keys and values it can have
type MagicType = Partial<{[K in keyof ResultCollection]: ResultFunc<K> | boolean}>;
// examples
const good1: MagicType = { // ok
A: true,
B: (r: { b: boolean }) => {
console.log(r);
},
};
const good2: MagicType = { // ok
A: (r: { a: string }) => {},
B: (r: { b: boolean }) => {
console.log(r);
},
C: false
};
const bad1: MagicType = {
Foo: true, // error
}
const bad2: MagicType = {
B: (r: { c: number }) => { // error
console.log('not a good b');
},
};