Closed
Description
TypeScript Version: 2.8.0-dev.20180216
Code
// Type-level filters to extract just the required or optional properties of a type
// Defns from https://github.com/Microsoft/TypeScript/pull/21919#issuecomment-365491689
type RequiredPropNames<T> = { [P in keyof T]: undefined extends T[P] ? never : P }[keyof T];
type OptionalPropNames<T> = { [P in keyof T]: undefined extends T[P] ? P : never }[keyof T];
type RequiredProps<T> = { [P in RequiredPropNames<T>]: T[P] };
type OptionalProps<T> = { [P in OptionalPropNames<T>]: T[P] };
// Some object types with different numbers of props
type P2 = {a: string, b: number}; // Two props
type P1 = {a: string}; // One prop
type P0 = {}; // No props
// Let's extract only the required properties of P0, P1, and P2
type P2Names = RequiredPropNames<P2>; // P2Names = "a" | "b" ✓😊
type P1Names = RequiredPropNames<P1>; // P1Names = "a" ✓😊
type P0Names = RequiredPropNames<P0>; // P0Names = any ?😕
type P2Props = RequiredProps<P2>; // P2Props = { a: string; b: number; } ✓😊
type P1Props = RequiredProps<P1>; // P1Props = { a: string; } ✓😊
type P0Props = RequiredProps<P0>; // P0Props = { [x: string]: any } ?😕
Expected behavior:
P0Names = never
and P0Props = {}
Actual behavior:
P0Names = any
and P0Props = {[x: string]: any}
Notes:
@ahejlsberg helpfully came up with the RequiredProps
and OptionalProps
definitions above from a question I asked in #21919. They work great except when T = {}
Not 100% sure if this is a bug or not, but it's definitely counterintuitive. RequiredProps
works for any object type except the empty one, when a string indexer suddenly appears. It certainly breaks the semantic expectations of RequiredProps
.
Workaround:
The following version of RequiredPropNames
achieves the expected behaviour by singling out the {}
case:
type RequiredPropNames<T> =
keyof T extends never
? never
: {[P in keyof T]: undefined extends T[P] ? never : P}[keyof T];
Metadata
Metadata
Assignees
Labels
No labels