Closed
Description
TypeScript Version: v3.5.1
Search Terms: union partial type assignable structure
Code
// Data types
interface Key {
type: "key";
key: string;
}
interface KeyValue {
type: "key-value";
key: string;
value: string;
}
type Either = Key | KeyValue;
type Common = Pick<Either, "type" | "key">;
// Test cases
// Structurally-compatible assignment
const either: PartialEither = null as any as Common;
// Type-guarding
if (either.type === "key-value") {
either.value;
}
// Problematic type definitions
// Using this intermediate type so I can still rely on 'type' as the discriminant property of
// the PartialEither type. The naive
// Pick<Either, "type" | "key"> & Partial<Omit<Either, "type" | "key">>
// would lose the relationship between `type` and shape, causing the type guard above to fail.
type _PartialEither<T extends Either> = Pick<T, "type" | "key"> & Partial<Omit<T, "type" | "key">>;
type PartialKey = _PartialEither<Key>;
type PartialKeyValue = _PartialEither<KeyValue>;
type PartialEither = PartialKey | PartialKeyValue;
// This type seems to force the compiler to reinterpret the partial types into a slightly
// different representation that's logically equivalent but properly assignable. From:
// https://stackoverflow.com/questions/57780109/union-of-partial-types-in-typescript-cant-be-type-narrowed
type Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never;
// Change the definition to this version to see the test cases above work.
// type PartialEither = Expand<PartialKey | PartialKeyValue>;
Expected behavior: Line 21, the structurally-compatible assignment, compiles, without a workaround. Using the Expand
type has no effect on assignability.
Actual behavior: Line 21 does not compile, stating
Type 'Pick<Either, "key" | "type">' is not assignable to type 'PartialEither'.
Type 'Pick<Either, "key" | "type">' is not assignable to type '_PartialEither<KeyValue>'.
Type 'Pick<Either, "key" | "type">' is not assignable to type 'Pick<KeyValue, "key" | "type">'.
Types of property 'type' are incompatible.
Type '"key" | "key-value"' is not assignable to type '"key-value"'.
Type '"key"' is not assignable to type '"key-value"'.
Using the Expand
helper type workaround fixes the assignability problem.
Playground Link: link