I'm trying to get this example to work like this:
interface Foo {
a: number;
b: string;
c: boolean;
}
type Explode<T> = keyof T extends infer K
? K extends unknown
? { [I in keyof T]: I extends K ? T[I] : never }
: never
: never;
type Test = Explode<Foo>;
const test: Test = {a: 1};
which gives me the following error:
Type '{ a: number; }' is not assignable to type '{ a: number; b: never; c: never; } | { a: never; b: string; c: never; } | { a: never; b: never; c: boolean; }'.
Type '{ a: number; }' is missing the following properties from type '{ a: number; b: never; c: never; }': b, c
How can I instantiate an object of type Test
without the error? I want a type that can contain either field a
or b
or c
(or be an empty {}
).
CodePudding user response:
I found the solution. I needed to make Foo
s fields optional like this:
EDITED to include the suggestion by @aprokryfos
interface Foo {
a: number;
b: string;
c: boolean;
}
Now I can do:
interface Foo {
a: number;
b: string;
c: boolean;
}
type AtMostOneOf<T> = keyof T extends infer K
? K extends unknown
? { [I in keyof T] ?: I extends K ? T[I] : never }
: never
: never;
type Test = AtMostOneOf<Foo>;
const test: Test = {a: 1}; // allowed
const test1: Test = {b: 'asd'}; // allowed
const test2: Test = {a: 1, b: 'asd'}; // not allowed
const test3: Test = {} // allowed
CodePudding user response:
You can define the Explode
type as:
type Explode<T> = {
[K in keyof T]: {
[key in K]: T[K]
}
}[keyof T]
then type Test
will become:
type Test = {
a: number;
} | {
b: string;
} | {
c: boolean;
}