-
-
Notifications
You must be signed in to change notification settings - Fork 679
UnionToTuple: Fix behavior when a union member is a supertype of another; Add ExcludeExactly type
#1349
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
UnionToTuple: Fix behavior when a union member is a supertype of another; Add ExcludeExactly type
#1349
Changes from all commits
ccb31dd
397ebf4
167f3eb
08bc31c
b24f977
0dbb9c0
7ef927a
e95c1ee
3b54932
8b984c9
4baac7e
0699dc0
01cbb49
408b2de
614ad2a
e4bd1e8
65cc0bd
68c4798
78fcde9
6822834
5dce64e
9579825
2d728ee
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| import type {IsNever} from './is-never.d.ts'; | ||
| import type {IsAny} from './is-any.d.ts'; | ||
| import type {If} from './if.d.ts'; | ||
| import type {IsEqual} from './is-equal.d.ts'; | ||
| import type {IfNotAnyOrNever} from './internal/type.d.ts'; | ||
|
|
||
| /** | ||
| A stricter version of `Exclude<T, U>` that excludes types only when they are exactly identical. | ||
| @example | ||
| ``` | ||
| import type {ExcludeExactly} from 'type-fest'; | ||
| type TestExclude1 = Exclude<'a' | 'b' | 'c' | 1 | 2 | 3, string>; | ||
| //=> 1 | 2 | 3 | ||
| type TestExcludeExactly1 = ExcludeExactly<'a' | 'b' | 'c' | 1 | 2 | 3, string>; | ||
| //=> 'a' | 'b' | 'c' | 1 | 2 | 3 | ||
| type TestExclude2 = Exclude<'a' | 'b' | 'c' | 1 | 2 | 3, any>; | ||
| //=> never | ||
| type TestExcludeExactly2 = ExcludeExactly<'a' | 'b' | 'c' | 1 | 2 | 3, any>; | ||
| //=> 'a' | 'b' | 'c' | 1 | 2 | 3 | ||
| type TestExclude3 = Exclude<{a: string} | {a: string; b: string}, {a: string}>; | ||
| //=> never | ||
| type TestExcludeExactly3 = ExcludeExactly<{a: string} | {a: string; b: string}, {a: string}>; | ||
| //=> {a: string; b: string} | ||
| ``` | ||
| @category Improved Built-in | ||
| */ | ||
| export type ExcludeExactly<Union, Delete> = | ||
| IfNotAnyOrNever< | ||
| Union, | ||
| _ExcludeExactly<Union, Delete>, | ||
| // If `Union` is `any`, then if `Delete` is `any`, return `never`, else return `Union`. | ||
| If<IsAny<Delete>, never, Union>, | ||
| // If `Union` is `never`, then if `Delete` is `never`, return `never`, else return `Union`. | ||
| If<IsNever<Delete>, never, Union> | ||
| >; | ||
|
|
||
| type _ExcludeExactly<Union, Delete> = | ||
| IfNotAnyOrNever<Delete, | ||
| Union extends unknown // For distributing `Union` | ||
| ? [Delete extends unknown // For distributing `Delete` | ||
| ? If<IsEqual<Union, Delete>, true, never> | ||
| : never] extends [never] ? Union : never | ||
| : never, | ||
| // If `Delete` is `any` or `never`, then return `Union`, | ||
| // because `Union` cannot be `any` or `never` here. | ||
| Union, Union | ||
| >; | ||
|
|
||
| export {}; |
taiyakihitotsu marked this conversation as resolved.
Show resolved
Hide resolved
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,3 @@ | ||
| import type {IsNever} from './is-never.d.ts'; | ||
| /** | ||
| Returns a boolean for whether the two given types are equal. | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please create another PR for the purpose of exposing
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Keep this PR only for fixing
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. Note:
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here is the expected workflow:
How does this look?
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Didn't get this. Can't see
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry for the confusion, I meant this part here: https://github.com/sindresorhus/type-fest/pull/1368/changes#diff-6ce133a34fd79b9e6b57c337b69241c8a393399e40f7e37e64bba9e421d7c2b0R31 I've edited the comment to clear it. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| import {expectType} from 'tsd'; | ||
| import type {ExcludeExactly} from '../index.d.ts'; | ||
|
|
||
| expectType<number>({} as ExcludeExactly<number, '1'>); | ||
| expectType<never>({} as ExcludeExactly<number, number>); | ||
| expectType<0>({} as ExcludeExactly<0, number>); | ||
| expectType<string>({} as ExcludeExactly<string, '1'>); | ||
| expectType<never>({} as ExcludeExactly<string, string>); | ||
| expectType<'0'>({} as ExcludeExactly<'0', string>); | ||
|
|
||
| expectType<{a: 0}>({} as ExcludeExactly<{a: 0} | {readonly a: 0}, {readonly a: 0}>); | ||
| expectType<{readonly a: 0}>({} as ExcludeExactly<{a: 0} | {readonly a: 0}, {a: 0}>); | ||
| expectType<never>({} as ExcludeExactly<{readonly a: 0}, {readonly a: 0}>); | ||
|
|
||
| // `never` excludes nothing | ||
| expectType<0 | 1 | 2>({} as ExcludeExactly<0 | 1 | 2, never>); | ||
| expectType<never>({} as ExcludeExactly<never, never>); | ||
som-sm marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| expectType<any>({} as ExcludeExactly<any, never>); | ||
| expectType<unknown>({} as ExcludeExactly<unknown, never>); | ||
|
|
||
| // Excluding from `unknown`/`any` | ||
| expectType<unknown>({} as ExcludeExactly<unknown, string>); | ||
| expectType<[unknown]>({} as ExcludeExactly<[unknown], [number]>); | ||
| expectType<unknown[]>({} as ExcludeExactly<unknown[], number[]>); | ||
| expectType<{a: unknown}>({} as ExcludeExactly<{a: unknown}, {a: number}>); | ||
| expectType<unknown[]>({} as ExcludeExactly<number[] | unknown[], number[]>); | ||
| expectType<any>({} as ExcludeExactly<any, string>); | ||
| expectType<[any]>({} as ExcludeExactly<[any], [number]>); | ||
| expectType<any[]>({} as ExcludeExactly<any[], number[]>); | ||
| expectType<{a: any}>({} as ExcludeExactly<{a: any}, {a: number}>); | ||
| expectType<any[]>({} as ExcludeExactly<number[] | any[], number[]>); | ||
|
|
||
| // Excluding `unknown`/`any` | ||
| expectType<never>({} as ExcludeExactly<unknown, unknown>); | ||
| expectType<never>({} as ExcludeExactly<any, any>); | ||
| expectType<unknown>({} as ExcludeExactly<unknown, any>); | ||
| expectType<any>({} as ExcludeExactly<any, unknown>); | ||
| expectType<string | number>({} as ExcludeExactly<string | number, unknown>); | ||
| expectType<string | number>({} as ExcludeExactly<string | number, any>); | ||
| expectType<never>({} as ExcludeExactly<never, any>); | ||
| expectType<never>({} as ExcludeExactly<never, unknown>); | ||
|
|
||
| // Unions | ||
| expectType<2>({} as ExcludeExactly<0 | 1 | 2, 0 | 1>); | ||
| expectType<never>({} as ExcludeExactly<0 | 1 | 2, 0 | 1 | 2>); | ||
| expectType<{readonly a?: 0}>({} as ExcludeExactly< | ||
| {a: 0} | {readonly a: 0} | {a?: 0} | {readonly a?: 0}, {a: 0} | {readonly a: 0} | {a?: 0} | ||
| >); | ||
| expectType<never>({} as ExcludeExactly< | ||
| {a: 0} | {readonly a: 0} | {a?: 0} | {readonly a?: 0}, {a: 0} | {readonly a: 0} | {a?: 0} | {readonly a?: 0} | ||
| >); | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this implementation simpler/better? It passes all the existing tests and also works as suggested with
anyandunknown.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your definition doesn't use
LastOfUnionand is clearer about the comparison part, since theSimpleIsEqualpattern is well-known, and all tests pass.But
SimpleIsEqualis already defined inis-equal.d.ts, so should we move it to internal, or even export it? Is that ok?(I vaguely think it is acceptable because this comparison construct is useful to define some type functions.)
And in this situation,
LastOfUnionis still worth exporting as a standalone utility.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#1349 (comment)