-
Notifications
You must be signed in to change notification settings - Fork 12.9k
Closed
Labels
BugA bug in TypeScriptA bug in TypeScriptFix AvailableA PR has been opened for this issueA PR has been opened for this issue
Milestone
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
AnyhowStep, jcalz and ardyfeb
Metadata
Metadata
Assignees
Labels
BugA bug in TypeScriptA bug in TypeScriptFix AvailableA PR has been opened for this issueA PR has been opened for this issue