Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Suggestion] Override and identifier! for Mapped Type and extend interface #18567

Closed
k8w opened this issue Sep 19, 2017 · 6 comments
Closed

[Suggestion] Override and identifier! for Mapped Type and extend interface #18567

k8w opened this issue Sep 19, 2017 · 6 comments
Labels
Question An issue which isn't directly actionable in code

Comments

@k8w
Copy link

k8w commented Sep 19, 2017

TypeScript Version: 2.4.0 / nightly (2.5.0-dev.201xxxxx)
2.5.2

Situation
There are lots of demand of more flexibility to Mapped Type. For example:

  1. Extend from a type and override some fields
  2. Turn optional field to required
  3. Only make some specific fields to optional

Suggestion

  1. Support multiple field definition in one type, the latter definition (same fields) will overwrite the previous
  2. Support identifier! for type definition which will remove null and undefined.

Code

type Full = {
  field1: string;
  field2: string;
  ....
  fieldN: string;
}

//Suggestion 1: Multiple fields in type definition and support override
type SomeOptional = {
  [P in keyof Full]: Full[P];  // extends
  [P in 'field1' | 'field2']? : Full[P];  // override field1 and field2 to optional
  xxx: string;  // and can add more fields.
}

//Suggestion 2: Identifer! to remove null and undefined
type SomeRequired = {
  [P in keyof SomeOptional]: SomeOptional[P];  // extends
  field1: SomeOptional[field1]!;  // ! will remove null and undefined from a type
}

Benifit

  1. It can resolve most problem with required and optional in current open issues. (like Add a new type Required #15012, Type to invert optionality of properties #16173, Mapped type optional -> required property issue #13224, Is this possible? Opposite of Partial Type #12578, )
  2. More flexible to extend and override, in limited complexity.

BTW, function getProperty in #12578 is a solution, but it cannot be used in declaration files.(.d.ts)

@mhegazy
Copy link
Contributor

mhegazy commented Sep 19, 2017

can you give us some use cases of each of these requests?

@k8w
Copy link
Author

k8w commented Sep 20, 2017

@mhegazy
Case for CRUD db doc

//A database doc type, for get
interface DocForGet {
  id: number;  //must have id on exist doc, auto generated by db.
  updatedTime: string | null | undefined;  //only have updatedTime if it have been updated (except first create)
  field1: xxx;
  ...
  fieldN: xxx;
}

So for DocForGet:

  1. When you insert a new doc to db, id is optional, and can't have updatedTime.
  2. When you update a doc, updatedTime must have a value (can't be null or undefined).

With this request, you can achieve this by:

type DocForInsert = {
  [P in keyof DocForGet]: DocForGet[P];  // extend
  id?: DocForInsert[id];  // override id to optional
  updatedTime?: never;  // can't set updatedTime on first create
}

type DocForUpdate = {
  [P in keyof DocForGet]: DocForGet[P];  // extend
  updatedTime: DocForGet[updatedTime]!;  //override, identifier! would remove null and undefined
}

@k8w k8w changed the title [Suggestion] Override and identifier! for Mapped Type [Suggestion] Override and identifier! for Mapped Type and extend interface Sep 20, 2017
@mhegazy
Copy link
Contributor

mhegazy commented Sep 21, 2017

The first portion can be defined by using intersection types, (try it on typescript@next):

type DocForInsert = DocForGet & {
    id?: DocForGet["id"];  // override id to optional
    updatedTime: never;  // force it to be never
};

the second part is tracked by #17948.

Assuming that id and updatedTime have the same type across all data types, can you have something like:

type Insert<T> = T & {
    id?: number;
    updatedTime: never;
};

type Get<T> = T & {
    id: number;
    updatedTime: string | null | undefined;
};

type Update<T> = T & {
    id: number;
    updatedTime: string;
};

@k8w
Copy link
Author

k8w commented Sep 22, 2017

@mhegazy

Partion 1, tried on typescript@2.6.0-dev.20170921, but get error:

interface DocForGet {
    id: number;  //must have id on exist doc, auto generated by db.
    updatedTime: string | null | undefined;  //only have updatedTime if it have been updated (except first create)
    field1: string;
}

type DocForInsert = DocForGet & {
    id?: DocForGet["id"];  // override id to optional
    updatedTime: never;  // force it to be never
};

let test: DocForInsert = {
    field1: 'test',
}

Error:

test.ts(12,5): error TS2322: Type '{ field1: string; }' is not assignable to type 'DocForInsert'.
Type '{ field1: string; }' is not assignable to type 'DocForGet'.
Property 'id' is missing in type '{ field1: string; }'.

@mhegazy
Copy link
Contributor

mhegazy commented Sep 22, 2017

You are correct, i was only thinking of the read part of it not the write. for that you need to use Overwrite.

type Diff<T extends string, U extends string> = ({ [P in T]: P } & { [P in U]: never } & { [x: string]: never })[T];
type Omit<T, K extends keyof T> = { [P in Diff<keyof T, K>]: T[P] };
type Overwrite<T, U> = { [P in Diff<keyof T, keyof U>]: T[P] } & U;

interface DocForGet {
    id: number;  //must have id on exist doc, auto generated by db.
    updatedTime: string | null | undefined;  //only have updatedTime if it have been updated (except first create)
    field1: string;
}

type DocForInsert = Overwrite<DocForGet, { id?: DocForGet["id"]; updatedTime?: never }>;

let test: DocForInsert = {
    field1: 'test',
};

@mhegazy mhegazy added the Question An issue which isn't directly actionable in code label Nov 21, 2017
@typescript-bot
Copy link
Collaborator

Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Question An issue which isn't directly actionable in code
Projects
None yet
Development

No branches or pull requests

3 participants