Skip to content

RequiredProps<T>: Unexpected result when T ={} #21988

@yortus

Description

@yortus

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-365491689typeRequiredPropNames<T>={[PinkeyofT]: undefinedextendsT[P] ? never : P}[keyofT];typeOptionalPropNames<T>={[PinkeyofT]: undefinedextendsT[P] ? P : never}[keyofT];typeRequiredProps<T>={[PinRequiredPropNames<T>]: T[P]};typeOptionalProps<T>={[PinOptionalPropNames<T>]: T[P]};// Some object types with different numbers of propstypeP2={a: string,b: number};// Two propstypeP1={a: string};// One proptypeP0={};// No props// Let's extract only the required properties of P0, P1, and P2typeP2Names=RequiredPropNames<P2>;// P2Names = "a" | "b" ✓😊typeP1Names=RequiredPropNames<P1>;// P1Names = "a" ✓😊typeP0Names=RequiredPropNames<P0>;// P0Names = any ?😕typeP2Props=RequiredProps<P2>;// P2Props ={a: string; b: number} ✓😊typeP1Props=RequiredProps<P1>;// P1Props ={a: string} ✓😊typeP0Props=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:

typeRequiredPropNames<T>=keyofTextendsnever ? never : {[PinkeyofT]: undefinedextendsT[P] ? never : P}[keyofT];

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions