-
Notifications
You must be signed in to change notification settings - Fork 13k
Closed
Labels
Working as IntendedThe behavior described is the intended behavior; this is not a bugThe behavior described is the intended behavior; this is not a bug
Description
Minimal repro:
type Oops<T> =
[T] extends [infer U]
? (U extends unknown ? [U, T] : never)
: never;
type X = Oops<"hello" | "world">;
Actual
type X = ["hello", "hello"] | ["world", "world"]
Expected
type X = ["hello", "hello" | "world"] | ["world", "hello" | "world"]
Use-case:
I want to define a type Exclusify
that defines mutually exclusive properties, much in the same way that object literal type normalization does. So
type Foo = Exclusify<"hello" | "world", number>;
should be equivalent to
type Foo =
| { hello: number, world: undefined }
| { hello: undefined, world: number };
I can do this by defining Exclusify
with a generic default to avoid distributing on Keys
.
type Exclusify<Keys extends keyof any, V, SingleKey = Keys> =
// Distribute on SingleKey
SingleKey extends unknown
? { [K in Keys]: K extends SingleKey ? V : undefined }
: never;
type Foo = Exclusify<"hello" | "world", string>
This works! But I'd rather not use a generic with a default. Instead, I figured I could add a conditional type.
type Exclusify<Keys extends keyof any, V> =
// Introduce SingleKey
[Keys] extends [infer SingleKey]
// // Distribute on SingleKey
? SingleKey extends unknown
? { [K in Keys]: K extends SingleKey ? V : undefined }
: never
: never
type Foo = Exclusify<"hello" | "world", string>
This, uh, doesn't work? It gives me
type Foo =
| { hello: string; }
| { world: string; }
Re-introducing SingleKey
as a defaulted type parameter and replacing the infer SingleKey
with unknown
fixes it.
type Exclusify<Keys extends keyof any, V, SingleKey = Keys> =
[Keys] extends [unknown]
// // Distribute on SingleKey
? SingleKey extends unknown
? { [K in Keys]: K extends SingleKey ? V : undefined }
: never
: never
type Foo = Exclusify<"hello" | "world", string>
It doesn't make sense to me why [Keys]
gets distributed in the presence of an infer
.
jcalz
Metadata
Metadata
Assignees
Labels
Working as IntendedThe behavior described is the intended behavior; this is not a bugThe behavior described is the intended behavior; this is not a bug