-
-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(react): add OmitKeyof safely and test cases for it (#816)
# Overview <!-- A clear and concise description of what this pr is about. --> related with TanStack/query#7139 This change update `OmitKeyof` to validate Omitting keys should be keyof Omitted Object Type As shown below, the inconvenience of having to manually remove unnecessary fields can be identified early on at the type level with `OmitKeyof`. ### Motivatings - https://github.com/TanStack/query/pull/6907/files - TanStack/query#7139 (comment) ### Expectation I made test-d to type-test `OmitKeyof` - For complex types using `Omit`, I want to reduce the difficulty of library maintenance for it. - `OmitKeyof` provide autocomplete by extending keyof TObject ![Mar-22-2024 18-12-27](https://github.com/TanStack/query/assets/61593290/3a3b1743-99cc-41bd-9f51-0a24f8973f87) - `OmitKeyof` check strictly second type parameter of it by extending keyof TObject ![Mar-22-2024 18-13-45](https://github.com/TanStack/query/assets/61593290/4204a036-af4f-4d19-b9fe-3aac39069f90) - `OmitKeyof` prevent library contributors make misspell like queryTey or qeuryKey ![Mar-22-2024 18-14-35](https://github.com/TanStack/query/assets/61593290/6c7e3ab0-a7bf-420f-a353-292e94297ec2) - Library users can be supplied with stable types consistently ## PR Checklist - [x] I did below actions if need 1. I read the [Contributing Guide](https://github.com/suspensive/react/blob/main/CONTRIBUTING.md) 2. I added documents and tests.
- Loading branch information
Showing
9 changed files
with
222 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@suspensive/react": patch | ||
--- | ||
|
||
fix(react): add OmitKeyof safely and test cases for it |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
import { describe, expectTypeOf, it } from 'vitest' | ||
import type { OmitKeyof } from './OmitKeyof' | ||
|
||
describe('OmitKeyof', () => { | ||
it("'s string key type check", () => { | ||
type A = { | ||
x: string | ||
y: number | ||
} | ||
|
||
type ExpectedType = { | ||
x: string | ||
} | ||
|
||
// Bad point | ||
// 1. original Omit can use 'z' as type parameter with no type error | ||
// 2. original Omit have no auto complete for 2nd type parameter | ||
expectTypeOf<Omit<A, 'z' | 'y'>>().toEqualTypeOf<ExpectedType>() | ||
|
||
// Solution | ||
|
||
// 1. strictly | ||
expectTypeOf< | ||
OmitKeyof< | ||
A, | ||
// OmitKeyof can't use 'z' as type parameter with type error because A don't have key 'z' | ||
// @ts-expect-error Type does not satisfy the constraint keyof A | ||
'z' | 'y' | ||
> | ||
>().toEqualTypeOf<ExpectedType> | ||
expectTypeOf< | ||
OmitKeyof< | ||
A, | ||
// OmitKeyof can't use 'z' as type parameter with type error because A don't have key 'z' | ||
// @ts-expect-error Type does not satisfy the constraint keyof A | ||
'z' | 'y', | ||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-arguments | ||
'strictly' | ||
> | ||
>().toEqualTypeOf<ExpectedType> | ||
|
||
// 2. safely | ||
expectTypeOf< | ||
OmitKeyof< | ||
A, | ||
// OmitKeyof can't use 'z' as type parameter type error with strictly parameter or default parameter | ||
// @ts-expect-error Type does not satisfy the constraint keyof A | ||
'z' | 'y' | ||
> | ||
>().toEqualTypeOf<ExpectedType> | ||
expectTypeOf< | ||
OmitKeyof< | ||
A, | ||
// With 'safely', OmitKeyof can use 'z' as type parameter like original Omit but This support autocomplete too yet for DX. | ||
'z' | 'y', | ||
'safely' | ||
> | ||
>().toEqualTypeOf<ExpectedType> | ||
}) | ||
|
||
it("'s number key type check", () => { | ||
type A = { | ||
[1]: string | ||
[2]: number | ||
} | ||
|
||
type ExpectedType = { | ||
[1]: string | ||
} | ||
|
||
// Bad point | ||
// 1. original Omit can use 3 as type parameter with no type error | ||
// 2. original Omit have no auto complete for 2nd type parameter | ||
expectTypeOf<Omit<A, 3 | 2>>().toEqualTypeOf<ExpectedType>() | ||
|
||
// Solution | ||
|
||
// 1. strictly | ||
expectTypeOf< | ||
OmitKeyof< | ||
A, | ||
// OmitKeyof can't use 3 as type parameter with type error because A don't have key 3 | ||
// @ts-expect-error Type does not satisfy the constraint keyof A | ||
3 | 2 | ||
> | ||
>().toEqualTypeOf<ExpectedType> | ||
expectTypeOf< | ||
OmitKeyof< | ||
A, | ||
// OmitKeyof can't use 3 as type parameter with type error because A don't have key 3 | ||
// @ts-expect-error Type does not satisfy the constraint keyof A | ||
3 | 2, | ||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-arguments | ||
'strictly' | ||
> | ||
>().toEqualTypeOf<ExpectedType> | ||
|
||
// 2. safely | ||
expectTypeOf< | ||
OmitKeyof< | ||
A, | ||
// OmitKeyof can't use 3 as type parameter type error with strictly parameter or default parameter | ||
// @ts-expect-error Type does not satisfy the constraint keyof A | ||
3 | 2 | ||
> | ||
>().toEqualTypeOf<ExpectedType> | ||
expectTypeOf< | ||
OmitKeyof< | ||
A, | ||
// With 'safely', OmitKeyof can use 3 as type parameter like original Omit but This support autocomplete too yet for DX. | ||
3 | 2, | ||
'safely' | ||
> | ||
>().toEqualTypeOf<ExpectedType> | ||
}) | ||
|
||
it("'s symbol key type check", () => { | ||
const symbol1 = Symbol() | ||
const symbol2 = Symbol() | ||
const symbol3 = Symbol() | ||
|
||
type A = { | ||
[symbol1]: string | ||
[symbol2]: number | ||
} | ||
|
||
type ExpectedType = { | ||
[symbol1]: string | ||
} | ||
|
||
// Bad point | ||
// 1. original Omit can use symbol3 as type parameter with no type error | ||
// 2. original Omit have no auto complete for 2nd type parameter | ||
expectTypeOf<Omit<A, typeof symbol3 | typeof symbol2>>().toEqualTypeOf<ExpectedType>() | ||
|
||
// Solution | ||
|
||
// 1. strictly | ||
expectTypeOf< | ||
OmitKeyof< | ||
A, | ||
// OmitKeyof can't use symbol3 as type parameter with type error because A don't have key symbol3 | ||
// @ts-expect-error Type does not satisfy the constraint keyof A | ||
typeof symbol3 | typeof symbol2 | ||
> | ||
>().toEqualTypeOf<ExpectedType> | ||
expectTypeOf< | ||
OmitKeyof< | ||
A, | ||
// OmitKeyof can't use symbol3 as type parameter with type error because A don't have key symbol3 | ||
// @ts-expect-error Type does not satisfy the constraint keyof A | ||
typeof symbol3 | typeof symbol2, | ||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-arguments | ||
'strictly' | ||
> | ||
>().toEqualTypeOf<ExpectedType> | ||
|
||
// 2. safely | ||
expectTypeOf< | ||
OmitKeyof< | ||
A, | ||
// OmitKeyof can't use symbol3 as type parameter type error with strictly parameter or default parameter | ||
// @ts-expect-error Type does not satisfy the constraint keyof A | ||
typeof symbol3 | typeof symbol2 | ||
> | ||
>().toEqualTypeOf<ExpectedType> | ||
expectTypeOf< | ||
OmitKeyof< | ||
A, | ||
// With 'safely', OmitKeyof can use symbol3 as type parameter like original Omit but This support autocomplete too yet for DX. | ||
typeof symbol3 | typeof symbol2, | ||
'safely' | ||
> | ||
>().toEqualTypeOf<ExpectedType> | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
export type OmitKeyof< | ||
TObject, | ||
TKey extends TStrictly extends 'safely' | ||
? | ||
| keyof TObject | ||
| (string & Record<never, never>) | ||
| (number & Record<never, never>) | ||
| (symbol & Record<never, never>) | ||
: keyof TObject, | ||
TStrictly extends 'strictly' | 'safely' = 'strictly', | ||
> = Omit<TObject, TKey> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
export type { PropsWithDevMode } from './PropsWithDevMode' | ||
export type { OmitKeyOf } from './OmitKeyOf' | ||
export type { OmitKeyof } from './OmitKeyof' | ||
export type { ConstructorType } from './ConstructorType' | ||
export type { Nullable } from './Nullable' |
Oops, something went wrong.