'Typescript a function returns union type isn't compatible with union of functions
Sorry maybe my question title isn't accurate, and I know a function return union is not equivalent to union of functions.
But my case is simple enough and I think it should be correct.
type Functions = (() => 'a') | (() => 'b')
type Returns = 'a' | 'b'
const buildFunction = (returnValue: Returns): Functions => () => returnValue
Because the input is one of 'a' | 'b', so the built function should also be one of (() => 'a') | (() => 'b').
However there is an error:
Type '() => Returns' is not assignable to type 'Functions'.
Type '() => Returns' is not assignable to type '() => "a"'.
Type 'Returns' is not assignable to type '"a"'.
Type '"b"' is not assignable to type '"a"'.
Update:
Thanks @jsejcksn, your solution is great for the above scenario. But my actual scenario is more complex than it, and this solution doesn't seem to solve my problem. Here is what I am facing on:
type MyEvent = { type: 'a' } | { type: 'b' }
type WithToJson<T> = T extends unknown
? T & { toJSON: () => T }
: never
// will be { type: 'a'; toJSON: () => { type: 'a' } } | { type: 'b'; toJSON: () => { type: 'b' } }
// adding a `toJSON` method to each event
const addToJson = (event: MyEvent): WithToJson<MyEvent> => ({
...event,
toJSON: () => event // error
})
// try to use generic function to restrict type, but doesn't work
const addToJsonGeneric1 = <TEvent extends MyEvent>(event: TEvent): WithToJson<MyEvent> => ({
...event,
toJSON: () => event
})
// give the generic type to WithToJson type
const addToJsonGeneric2 = <TEvent extends MyEvent>(event: TEvent): WithToJson<TEvent> => ({
...event,
toJSON: () => event
})
Solution 1:[1]
Update in response to your comment and updated question:
I now think that this is what you're looking for: a function for assigning a JSON transformer function to an object:
type Json = boolean | null | number | string | Json[] | { [key: string]: Json };
type Serializer<T, Serializable extends Json = Json> = (value: T) => Serializable;
type Producer<T> = () => T;
type ToJSONMethod<T> = { toJSON: Producer<T> };
type WithCustomJSON<T extends object, Serializable extends Json> = T & ToJSONMethod<Serializable>;
function makeSerializable <T extends object>(o: T): asserts o is T & ToJSONMethod<T>;
function makeSerializable <
T extends object,
Serializable extends Json,
>(o: T, serializer: Serializer<T, Serializable>): void;
function makeSerializable <
T extends object,
Serializable extends Json,
>(o: T, serializer?: Serializer<T, Serializable>): void {
Object.defineProperty(o, 'toJSON', {
configurable: true,
enumerable: false,
writable: true,
value: serializer ? () => serializer(o) : () => o,
});
}
// Usage example:
const evA = { type: 'a' as const };
makeSerializable(evA, ev => ({type: ev.type, message: 'This was serialized!'}));
evA.toJSON(); /* ?
~~~~~~
Property 'toJSON' does not exist on type '{ type: "a"; }'.(2339) */
console.log(Object.keys(evA)); // ["type"]
console.log(JSON.stringify(evA)); // {"type":"a","message":"This was serialized!"}
makeSerializable(evA);
evA.toJSON(); // ok ?
console.log(Object.keys(evA)); // ["type"]
console.log(JSON.stringify(evA)); // {"type":"a"}
Original answer:
If you are trying to restrict the types of arguments that can be provided to the function creator, then you can do it using a generic like this:
type ReturnValue = 'a' | 'b';
const buildFunction = <R extends ReturnValue>(value: R) => (): R => value;
const fnA = buildFunction('a'); // () => "a"
const fnB = buildFunction('b'); // () => "b"
const a = fnA(); // "a"
const b = fnB(); // "b"
const fnC = buildFunction('c'); /*
~~~
Argument of type '"c"' is not assignable to parameter of type 'ReturnValue'.(2345) */
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
