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

Maintain modifiers on Omit #31205

Merged
merged 4 commits into from May 10, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 1 addition & 3 deletions src/lib/es5.d.ts
Expand Up @@ -1446,9 +1446,7 @@ type Extract<T, U> = T extends U ? T : never;
/**
* Construct a type with the properties of T except for those in type K.
*/
type Omit<T, K extends keyof any> = {
Copy link
Contributor

@jwbay jwbay May 1, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔Why can't this be K extends keyof T to make the type homomorphic?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't believe that would make the type homomorphic, would it @ahejlsberg?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, discussed offline, but for reasons outlined below, the fact that it's not just a simple keyof T or K (where K extends keyof T) means that TypeScript can't trivially look at that and say "yup, we gotta keep the modifiers around on this one".

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could also do

type Omit<T, K extends keyof any> = T extends unknown ? Pick<T, Exclude<keyof T, K>> : never;

that is to say: making the builtin Omit distributive. (Which'd give it an alias symbol, too) A bunch of library authors end up swapping their internal Omit to this once they get into situations involving unions.

Distributive conditional like this have perf issues when nested repeatedly right now, tho, sooooo.... ehhhhh?

[P in Exclude<keyof T, K>]: T[P]
};
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;

/**
* Exclude null and undefined from T
Expand Down
12 changes: 6 additions & 6 deletions tests/baselines/reference/genericIsNeverEmptyObject.types
Expand Up @@ -2,18 +2,18 @@
// Repro from #29067

function test<T extends { a: string }>(obj: T) {
>test : <T extends { a: string; }>(obj: T) => Omit<T, "a"> & { b: string; }
>test : <T extends { a: string; }>(obj: T) => Pick<T, Exclude<keyof T, "a">> & { b: string; }
>a : string
>obj : T

let { a, ...rest } = obj;
>a : string
>rest : Omit<T, "a">
>rest : Pick<T, Exclude<keyof T, "a">>
>obj : T

return { ...rest, b: a };
>{ ...rest, b: a } : Omit<T, "a"> & { b: string; }
>rest : Omit<T, "a">
>{ ...rest, b: a } : Pick<T, Exclude<keyof T, "a">> & { b: string; }
>rest : Pick<T, Exclude<keyof T, "a">>
>b : string
>a : string
}
Expand All @@ -30,7 +30,7 @@ let o2: { b: string, x: number } = test(o1);
>o2 : { b: string; x: number; }
>b : string
>x : number
>test(o1) : Omit<{ a: string; x: number; }, "a"> & { b: string; }
>test : <T extends { a: string; }>(obj: T) => Omit<T, "a"> & { b: string; }
>test(o1) : Pick<{ a: string; x: number; }, "x"> & { b: string; }
>test : <T extends { a: string; }>(obj: T) => Pick<T, Exclude<keyof T, "a">> & { b: string; }
>o1 : { a: string; x: number; }

16 changes: 8 additions & 8 deletions tests/baselines/reference/genericObjectRest.types
Expand Up @@ -16,32 +16,32 @@ function f1<T extends { a: string, b: number }>(obj: T) {
let { a: a1, ...r1 } = obj;
>a : any
>a1 : string
>r1 : Omit<T, "a">
>r1 : Pick<T, Exclude<keyof T, "a">>
>obj : T

let { a: a2, b: b2, ...r2 } = obj;
>a : any
>a2 : string
>b : any
>b2 : number
>r2 : Omit<T, "a" | "b">
>r2 : Pick<T, Exclude<keyof T, "a" | "b">>
>obj : T

let { 'a': a3, ...r3 } = obj;
>a3 : string
>r3 : Omit<T, "a">
>r3 : Pick<T, Exclude<keyof T, "a">>
>obj : T

let { ['a']: a4, ...r4 } = obj;
>'a' : "a"
>a4 : string
>r4 : Omit<T, "a">
>r4 : Pick<T, Exclude<keyof T, "a">>
>obj : T

let { [a]: a5, ...r5 } = obj;
>a : "a"
>a5 : string
>r5 : Omit<T, "a">
>r5 : Pick<T, Exclude<keyof T, "a">>
>obj : T
}

Expand All @@ -68,7 +68,7 @@ function f2<T extends { [sa]: string, [sb]: number }>(obj: T) {
>a1 : string
>sb : unique symbol
>b1 : number
>r1 : Omit<T, unique symbol | unique symbol>
>r1 : Pick<T, Exclude<keyof T, unique symbol | unique symbol>>
>obj : T
}

Expand All @@ -83,7 +83,7 @@ function f3<T, K1 extends keyof T, K2 extends keyof T>(obj: T, k1: K1, k2: K2) {
>a1 : T[K1]
>k2 : K2
>a2 : T[K2]
>r1 : Omit<T, K1 | K2>
>r1 : Pick<T, Exclude<keyof T, K1 | K2>>
>obj : T
}

Expand All @@ -104,7 +104,7 @@ function f4<K1 extends keyof Item, K2 extends keyof Item>(obj: Item, k1: K1, k2:
>a1 : Item[K1]
>k2 : K2
>a2 : Item[K2]
>r1 : Omit<Item, K1 | K2>
>r1 : Pick<Item, Exclude<"a", K1 | K2> | Exclude<"b", K1 | K2> | Exclude<"c", K1 | K2>>
>obj : Item
}

6 changes: 3 additions & 3 deletions tests/baselines/reference/literalTypeWidening.types
Expand Up @@ -443,14 +443,14 @@ function test<T extends { a: string, b: string }>(obj: T): T {

let { a, ...rest } = obj;
>a : string
>rest : Omit<T, "a">
>rest : Pick<T, Exclude<keyof T, "a">>
>obj : T

return { a: 'hello', ...rest } as T;
>{ a: 'hello', ...rest } as T : T
>{ a: 'hello', ...rest } : { a: string; } & Omit<T, "a">
>{ a: 'hello', ...rest } : { a: string; } & Pick<T, Exclude<keyof T, "a">>
>a : string
>'hello' : "hello"
>rest : Omit<T, "a">
>rest : Pick<T, Exclude<keyof T, "a">>
}

4 changes: 2 additions & 2 deletions tests/baselines/reference/mappedTypeConstraints.symbols
Expand Up @@ -132,9 +132,9 @@ const modifier = <T extends TargetProps>(targetProps: T) => {
>targetProps : Symbol(targetProps, Decl(mappedTypeConstraints.ts, 30, 41))

rest.foo;
>rest.foo : Symbol(foo)
>rest.foo : Symbol(foo, Decl(mappedTypeConstraints.ts, 25, 20))
>rest : Symbol(rest, Decl(mappedTypeConstraints.ts, 31, 13))
>foo : Symbol(foo)
>foo : Symbol(foo, Decl(mappedTypeConstraints.ts, 25, 20))

};

4 changes: 2 additions & 2 deletions tests/baselines/reference/mappedTypeConstraints.types
Expand Up @@ -98,12 +98,12 @@ const modifier = <T extends TargetProps>(targetProps: T) => {

let {bar, ...rest} = targetProps;
>bar : string
>rest : Omit<T, "bar">
>rest : Pick<T, Exclude<keyof T, "bar">>
>targetProps : T

rest.foo;
>rest.foo : T["foo"]
>rest : Omit<T, "bar">
>rest : Pick<T, Exclude<keyof T, "bar">>
>foo : T["foo"]

};
Expand Down
6 changes: 3 additions & 3 deletions tests/baselines/reference/objectRestNegative.types
Expand Up @@ -36,18 +36,18 @@ function stillMustBeLast({ ...mustBeLast, a }: { a: number, b: string }): void {
>b : string
}
function generic<T extends { x, y }>(t: T) {
>generic : <T extends { x: any; y: any; }>(t: T) => Omit<T, "x">
>generic : <T extends { x: any; y: any; }>(t: T) => Pick<T, Exclude<keyof T, "x">>
>x : any
>y : any
>t : T

let { x, ...rest } = t;
>x : any
>rest : Omit<T, "x">
>rest : Pick<T, Exclude<keyof T, "x">>
>t : T

return rest;
>rest : Omit<T, "x">
>rest : Pick<T, Exclude<keyof T, "x">>
}

let rest: { b: string }
Expand Down
27 changes: 27 additions & 0 deletions tests/baselines/reference/omitTypeHelperModifiers01.errors.txt
@@ -0,0 +1,27 @@
tests/cases/compiler/omitTypeHelperModifiers01.ts(16,7): error TS2540: Cannot assign to 'c' because it is a read-only property.


==== tests/cases/compiler/omitTypeHelperModifiers01.ts (1 errors) ====
type A = {
a: number;
b?: string;
readonly c: boolean;
d: unknown;
};

type B = Omit<A, 'a'>;

function f(x: B) {
const b = x.b;
x.b = "hello";
x.b = undefined;

const c = x.c;
x.c = true;
~
!!! error TS2540: Cannot assign to 'c' because it is a read-only property.

const d = x.d;
x.d = d;
}

34 changes: 34 additions & 0 deletions tests/baselines/reference/omitTypeHelperModifiers01.js
@@ -0,0 +1,34 @@
//// [omitTypeHelperModifiers01.ts]
type A = {
a: number;
b?: string;
readonly c: boolean;
d: unknown;
};

type B = Omit<A, 'a'>;

function f(x: B) {
const b = x.b;
x.b = "hello";
x.b = undefined;

const c = x.c;
x.c = true;

const d = x.d;
x.d = d;
}


//// [omitTypeHelperModifiers01.js]
"use strict";
function f(x) {
var b = x.b;
x.b = "hello";
x.b = undefined;
var c = x.c;
x.c = true;
var d = x.d;
x.d = d;
}
69 changes: 69 additions & 0 deletions tests/baselines/reference/omitTypeHelperModifiers01.symbols
@@ -0,0 +1,69 @@
=== tests/cases/compiler/omitTypeHelperModifiers01.ts ===
type A = {
>A : Symbol(A, Decl(omitTypeHelperModifiers01.ts, 0, 0))

a: number;
>a : Symbol(a, Decl(omitTypeHelperModifiers01.ts, 0, 10))

b?: string;
>b : Symbol(b, Decl(omitTypeHelperModifiers01.ts, 1, 14))

readonly c: boolean;
>c : Symbol(c, Decl(omitTypeHelperModifiers01.ts, 2, 15))

d: unknown;
>d : Symbol(d, Decl(omitTypeHelperModifiers01.ts, 3, 24))

};

type B = Omit<A, 'a'>;
>B : Symbol(B, Decl(omitTypeHelperModifiers01.ts, 5, 2))
>Omit : Symbol(Omit, Decl(lib.es5.d.ts, --, --))
>A : Symbol(A, Decl(omitTypeHelperModifiers01.ts, 0, 0))

function f(x: B) {
>f : Symbol(f, Decl(omitTypeHelperModifiers01.ts, 7, 22))
>x : Symbol(x, Decl(omitTypeHelperModifiers01.ts, 9, 11))
>B : Symbol(B, Decl(omitTypeHelperModifiers01.ts, 5, 2))

const b = x.b;
>b : Symbol(b, Decl(omitTypeHelperModifiers01.ts, 10, 9))
>x.b : Symbol(b, Decl(omitTypeHelperModifiers01.ts, 1, 14))
>x : Symbol(x, Decl(omitTypeHelperModifiers01.ts, 9, 11))
>b : Symbol(b, Decl(omitTypeHelperModifiers01.ts, 1, 14))

x.b = "hello";
>x.b : Symbol(b, Decl(omitTypeHelperModifiers01.ts, 1, 14))
>x : Symbol(x, Decl(omitTypeHelperModifiers01.ts, 9, 11))
>b : Symbol(b, Decl(omitTypeHelperModifiers01.ts, 1, 14))

x.b = undefined;
>x.b : Symbol(b, Decl(omitTypeHelperModifiers01.ts, 1, 14))
>x : Symbol(x, Decl(omitTypeHelperModifiers01.ts, 9, 11))
>b : Symbol(b, Decl(omitTypeHelperModifiers01.ts, 1, 14))
>undefined : Symbol(undefined)

const c = x.c;
>c : Symbol(c, Decl(omitTypeHelperModifiers01.ts, 14, 9))
>x.c : Symbol(c, Decl(omitTypeHelperModifiers01.ts, 2, 15))
>x : Symbol(x, Decl(omitTypeHelperModifiers01.ts, 9, 11))
>c : Symbol(c, Decl(omitTypeHelperModifiers01.ts, 2, 15))

x.c = true;
>x.c : Symbol(c, Decl(omitTypeHelperModifiers01.ts, 2, 15))
>x : Symbol(x, Decl(omitTypeHelperModifiers01.ts, 9, 11))
>c : Symbol(c, Decl(omitTypeHelperModifiers01.ts, 2, 15))

const d = x.d;
>d : Symbol(d, Decl(omitTypeHelperModifiers01.ts, 17, 9))
>x.d : Symbol(d, Decl(omitTypeHelperModifiers01.ts, 3, 24))
>x : Symbol(x, Decl(omitTypeHelperModifiers01.ts, 9, 11))
>d : Symbol(d, Decl(omitTypeHelperModifiers01.ts, 3, 24))

x.d = d;
>x.d : Symbol(d, Decl(omitTypeHelperModifiers01.ts, 3, 24))
>x : Symbol(x, Decl(omitTypeHelperModifiers01.ts, 9, 11))
>d : Symbol(d, Decl(omitTypeHelperModifiers01.ts, 3, 24))
>d : Symbol(d, Decl(omitTypeHelperModifiers01.ts, 17, 9))
}

72 changes: 72 additions & 0 deletions tests/baselines/reference/omitTypeHelperModifiers01.types
@@ -0,0 +1,72 @@
=== tests/cases/compiler/omitTypeHelperModifiers01.ts ===
type A = {
>A : A

a: number;
>a : number

b?: string;
>b : string | undefined

readonly c: boolean;
>c : boolean

d: unknown;
>d : unknown

};

type B = Omit<A, 'a'>;
>B : Pick<A, "b" | "c" | "d">

function f(x: B) {
>f : (x: Pick<A, "b" | "c" | "d">) => void
>x : Pick<A, "b" | "c" | "d">

const b = x.b;
>b : string | undefined
>x.b : string | undefined
>x : Pick<A, "b" | "c" | "d">
>b : string | undefined

x.b = "hello";
>x.b = "hello" : "hello"
>x.b : string | undefined
>x : Pick<A, "b" | "c" | "d">
>b : string | undefined
>"hello" : "hello"

x.b = undefined;
>x.b = undefined : undefined
>x.b : string | undefined
>x : Pick<A, "b" | "c" | "d">
>b : string | undefined
>undefined : undefined

const c = x.c;
>c : boolean
>x.c : boolean
>x : Pick<A, "b" | "c" | "d">
>c : boolean

x.c = true;
>x.c = true : true
>x.c : any
>x : Pick<A, "b" | "c" | "d">
>c : any
>true : true

const d = x.d;
>d : unknown
>x.d : unknown
>x : Pick<A, "b" | "c" | "d">
>d : unknown

x.d = d;
>x.d = d : unknown
>x.d : unknown
>x : Pick<A, "b" | "c" | "d">
>d : unknown
>d : unknown
}