Typescript: Removing optional modifier also removes `undefined` from value type

Created on 19 Apr 2019  ·  5Comments  ·  Source: microsoft/TypeScript


TypeScript Version: 3.3.3333


Search Terms: NonPartial, remove optional modifier

Code

// A *self-contained* demonstration of the problem follows...
// Test this by running `tsc` on the command-line, rather than through another build tool such as Gulp, Webpack, etc.

interface OptClass {
  opt?: number;
}

type NonPartialIsh = {[K in keyof OptClass]-?: OptClass[K] | undefined};

const test = {opt: undefined};

verify<NonPartialIsh>(test);  // should NOT be error, but shows error

function verify<T>(a: T) {}

Expected behavior:

  • Should not have any error.
  • NonPartialish.opt type should support undefined.

Actual behavior:

  • Throws the following error:
ERROR(12,23): : Argument of type '{ opt: undefined; }' is not assignable to parameter of type 'NonPartialIsh'.
  Types of property 'opt' are incompatible.
    Type 'undefined' is not assignable to type 'number'.
Transpiled code follows despite errors.
  • NonPartialish.opt type does not support undefined.

Playground Link: https://www.typescriptlang.org/play/#src=%2F%2F%20A%20self-contained%20demonstration%20of%20the%20problem%20follows...%0D%0A%2F%2F%20Test%20this%20by%20running%20%60tsc%60%20on%20the%20command-line%2C%20rather%20than%20through%20another%20build%20tool%20such%20as%20Gulp%2C%20Webpack%2C%20etc.%0D%0A%0D%0Ainterface%20OptClass%20%7B%0D%0A%20%20opt%3F%3A%20number%3B%0D%0A%7D%0D%0A%0D%0Atype%20NonPartialIsh%20%3D%20%7B%5BK%20in%20keyof%20OptClass%5D-%3F%3A%20OptClass%5BK%5D%20%7C%20undefined%7D%3B%0D%0A%0D%0Aconst%20test%20%3D%20%7Bopt%3A%20undefined%7D%3B%0D%0A%0D%0Averify%3CNonPartialIsh%3E(test)%3B%20%20%2F%2F%20should%20NOT%20be%20error%2C%20but%20shows%20error%0D%0A%0D%0Afunction%20verify%3CT%3E(a%3A%20T)%20%7B%20%7D%0D%0A

Related Issues:

Bug

Most helpful comment

Unfortunately missing and undefined aren't consistently two different things in TypeScript; see #13195.

Relevant documentation on the behavior of -?:

Note that in --strictNullChecks mode, when a homomorphic mapped type removes a ? modifier from a property in the underlying type it also removes undefined from the type of that property

But in

{[K in keyof T]-?: Foo<T[K]>}

should undefined be excluded from the original type of the property (T[K]) or the mapped type of the property (Foo<T[K]>)? I'd kind of expect it to be the former but it looks like it's actually the latter.

Assuming we can't destabilize the current behavior, someone who wants to strip the optional modifier off property keys but hold on to undefined in their values could do so by preventing the compiler from recognizing the mapped type as homomorphic:

type NullablyRequired<T> = { [P in (keyof T & keyof any)]: T[P] }

type Test = NullablyRequired<{a?: string, b: number}>
// type Test = {a: string | undefined, b: number}

All 5 comments

I believe -? removes undefined intentionally, because ? adds it.

Optional and undefined are two different things. Optional adds undefined which is expected.
But removing Optional shouldn't remove explicitly typed undefined (by union).

Unfortunately missing and undefined aren't consistently two different things in TypeScript; see #13195.

Relevant documentation on the behavior of -?:

Note that in --strictNullChecks mode, when a homomorphic mapped type removes a ? modifier from a property in the underlying type it also removes undefined from the type of that property

But in

{[K in keyof T]-?: Foo<T[K]>}

should undefined be excluded from the original type of the property (T[K]) or the mapped type of the property (Foo<T[K]>)? I'd kind of expect it to be the former but it looks like it's actually the latter.

Assuming we can't destabilize the current behavior, someone who wants to strip the optional modifier off property keys but hold on to undefined in their values could do so by preventing the compiler from recognizing the mapped type as homomorphic:

type NullablyRequired<T> = { [P in (keyof T & keyof any)]: T[P] }

type Test = NullablyRequired<{a?: string, b: number}>
// type Test = {a: string | undefined, b: number}

@weswigham I think it should at least work when you explicitly declare undefined to be a valid value, but it doesn't.

interface OptClass {
  opt?: number | undefined;
}

Wow had no idea there's -?... is there a way to remove | undefined somehow - so that something like string | undefined would become simply string?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

DanielRosenwasser picture DanielRosenwasser  ·  3Comments

MartynasZilinskas picture MartynasZilinskas  ·  3Comments

jbondc picture jbondc  ·  3Comments

Roam-Cooper picture Roam-Cooper  ·  3Comments

fwanicka picture fwanicka  ·  3Comments