question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

NonNullable isn't narrowing down object values' types for optional properties

See original GitHub issue

TypeScript Version: 3.2.0-dev.20181106

Search Terms: NonNullable object NonNullable object values

Code

Run the following code via tsc --no-emit --strict test.ts:

interface P {
  color?: 'red' | 'green';
}

type RequiredP = {
  [K in keyof P]: NonNullable<P[K]>;
}

declare const p: RequiredP;
const color: 'red' | 'green' = p.color;

Expected behavior:

The resulting type should not allow undefined for the value at a property color.

Actual behavior:

undefined is still allowed. Using the NonNullable type doesn’t seem to have any effect.

Playground Link: Note: you need to enable strictNullChecks manually! https://www.typescriptlang.org/play/#src=interface P { color%3F%3A ‘red’ | ‘green’%3B } type RequiredP %3D { [K in keyof P]%3A NonNullable<P[K]>%3B } declare const p%3A RequiredP%3B const color%3A ‘red’ | ‘green’ %3D p.color%3B

Related Issues:

Issue Analytics

  • State:open
  • Created 5 years ago
  • Reactions:8
  • Comments:9 (1 by maintainers)

github_iconTop GitHub Comments

26reactions
hiyelbazcommented, Sep 30, 2019

Here is the generic version:

type DeepNonNullable<T> = {
        [P in keyof T]-?: NonNullable<T[P]>;
}
12reactions
dragomirtitiancommented, Sep 19, 2019

@mgol Let me know if I misunderstood the problem, but I think this issue is just about the fact that your RequiredP is a homomorphic mapped type. This means that the optionality of the field is preserved in RequiredP If you use -? (added by this PR) you can remove the optional modifier explicitly and all will work as expected as far as I can tell:

interface P {
  color?: 'red' | 'green';
}

type RequiredP = {
  [K in keyof P]-?: NonNullable<P[K]>;
}

declare const p: RequiredP;
const color: 'red' | 'green' = p.color; // no error, previously an error because p.color still contained undefined

play

Note: Mapped types are only homomorphic if they map over keyof T where T is any type or if they map over K where K is a type parameter extending keyof T. This is why your workarounds worked because they broke the pattern for homomorphic mapped types.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Type an object with optional keys, but non-null value for keys ...
With --strictNullChecks turned on, the signature {[k: string]: SomeType} is saying that every single string-key property exists and is of type ...
Read more >
Documentation - Narrowing - TypeScript
The “true” branch narrows x 's types which have either an optional or required property value , and the “false” branch narrows to...
Read more >
Type-Safe TypeScript with Type Narrowing - Rainer Hahnekamp
Date is the type of input and not its value. ... Strictly speaking function is not a real type, it is a callable...
Read more >
When to use `never` and `unknown` in TypeScript
Types explained using set theory. When you get down to a fundamental definition a type is a set of possible values, and nothing...
Read more >
narrowing types via type guards and assertion functions - 2ality
Note that narrowing does not change the original type of value , it only ... @ts-ignore: Property 'name' does not exist on type...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found