Skip to content

Non null property in map type #19608

Closed
Closed
@aaronbeall

Description

@aaronbeall

TypeScript Version: 2.7.0-dev.201xxxxx

Feature request

It would be helpful when using strictNullChecks to have some way to map a type with optional fields to an equivalent type with non-nullable fields. I would expect using the ! non-null assertion like syntax somewhere in a map type:

  [P in K]: User[P]!;
  // or
  [P in K]!: User[P];

Why I want this

I've found that after turning on strictNullChecks I run into a lot of data types that have optional fields (in particular return values from http requests where all fields could be missing) that end up producing a lot of false flag "possibly null" errors in down-stream code. The problem is that even though I validate data at a high level, the data type holds onto the "possibly undefined" type wherever the data type is used in down-stream code, for example in my React component tree props. The truth is many of these components only get rendered when those fields are populated, but TS doesn't know this and probably couldn't possibly know this.

So the obvious solution is to create mirrors of the original data types that have certain fields defined as non-null... but this becomes quite cumbersome and leaky to maintain, especially since you rightfully can't assign something that has an optional field to something that doesn't.

In the end what I'm really trying to express is a type derived from a type with null | undefined removed from certain (or all) fields. Basically the opposite of Partial<>. And if I can do this generically I can eliminate a lot of the maintenance cost.

Code

// Example of a source type where anything could be undefined 
// (I encounter this a lot with swagger generated DTO types)
interface User {
  id?: string;
  name?: string;
}

// User with all fields made non-null
type CompleteUser {
  [P in keyof User]: User[P]!;
  // or
  [P in keyof User]!: User[P];
}

// Example
function isCompleteUser(user: User): user is CompleteUser {
  return user.id != null && user.name != null;
}

// Generic wrapper version
type Complete<T, K extends keyof T> = {
  [P in K]: T[P]!;
  // or
  [P in K]!: T[P];
}

// Example with generic validator type guard function
function isComplete<T, K extends keyof T>(obj: T, ...keys: K[]): obj is Complete<T, K> {
  return keys.every(key => obj[key] != null);
}

declare const user: User;
if (isComplete(user, "id", "name")) {
 // user = { id: string; name: string; }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    DuplicateAn existing issue was already created

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions