-
Notifications
You must be signed in to change notification settings - Fork 118
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: β add StrictDeepPick and StrictDeepOmit (#384)
* feat: β StrictDeepPick * feat: β StrictDeepOmit * docs: π Conventions * docs: π DeepOmit / DeepPick * docs: π changeset * docs: π add example for Strict* convention * docs: π add limitations to Strict* types * refactor: π DeepOmit improvements * refactor: π DeepPick improvements * test: π§ͺ generic type for DeepPick and DeepOmit * docs: π minor => major
- Loading branch information
Showing
17 changed files
with
1,678 additions
and
1,510 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,6 @@ | ||
--- | ||
"ts-essentials": major | ||
--- | ||
|
||
Added `StrictDeepOmit` and `StrictDeepPick` that support generic type and removed generic constraint on the second type | ||
parameter of `DeepOmit` and `DeepPick` |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,67 +1,46 @@ | ||
import { AnyRecord } from "../any-record"; | ||
import { Builtin } from "../built-in"; | ||
import { DeepModify } from "../deep-modify"; | ||
|
||
export type DeepOmit<Type, Filter extends DeepModify<Type>> = Type extends Builtin | ||
export type DeepOmit<Type, Filter> = Type extends Builtin | ||
? Type | ||
: Type extends Map<infer Keys, infer Values> | ||
? Filter extends Map<Keys, infer FilterValues> | ||
? FilterValues extends DeepModify<Values> | ||
? Map<Keys, DeepOmit<Values, FilterValues>> | ||
: Type | ||
? Map<Keys, DeepOmit<Values, FilterValues>> | ||
: Type | ||
: Type extends ReadonlyMap<infer Keys, infer Values> | ||
? Filter extends ReadonlyMap<Keys, infer FilterValues> | ||
? FilterValues extends DeepModify<Values> | ||
? ReadonlyMap<Keys, DeepOmit<Values, FilterValues>> | ||
: Type | ||
? ReadonlyMap<Keys, DeepOmit<Values, FilterValues>> | ||
: Type | ||
: Type extends WeakMap<infer Keys, infer Values> | ||
? Filter extends WeakMap<Keys, infer FilterValues> | ||
? FilterValues extends DeepModify<Values> | ||
? WeakMap<Keys, DeepOmit<Values, FilterValues>> | ||
: Type | ||
? WeakMap<Keys, DeepOmit<Values, FilterValues>> | ||
: Type | ||
: Type extends Set<infer Values> | ||
? Filter extends Set<infer FilterValues> | ||
? FilterValues extends DeepModify<Values> | ||
? Set<DeepOmit<Values, FilterValues>> | ||
: Type | ||
? Set<DeepOmit<Values, FilterValues>> | ||
: Type | ||
: Type extends ReadonlySet<infer Values> | ||
? Filter extends ReadonlySet<infer FilterValues> | ||
? FilterValues extends DeepModify<Values> | ||
? ReadonlySet<DeepOmit<Values, FilterValues>> | ||
: Type | ||
? ReadonlySet<DeepOmit<Values, FilterValues>> | ||
: Type | ||
: Type extends WeakSet<infer Values> | ||
? Filter extends WeakSet<infer FilterValues> | ||
? FilterValues extends DeepModify<Values> | ||
? WeakSet<DeepOmit<Values, FilterValues>> | ||
: Type | ||
? WeakSet<DeepOmit<Values, FilterValues>> | ||
: Type | ||
: Type extends Array<infer Values> | ||
? Filter extends Array<infer FilterValues> | ||
? FilterValues extends DeepModify<Values> | ||
? Array<DeepOmit<Values, FilterValues>> | ||
: Type | ||
? Array<DeepOmit<Values, FilterValues>> | ||
: Type | ||
: Type extends Promise<infer Value> | ||
? Filter extends Promise<infer FilterValue> | ||
? FilterValue extends DeepModify<Value> | ||
? Promise<DeepOmit<Value, FilterValue>> | ||
: Type | ||
? Promise<DeepOmit<Value, FilterValue>> | ||
: Type | ||
: Filter extends AnyRecord | ||
? { | ||
[Key in keyof Type as Key extends keyof Filter | ||
? Filter[Key] extends true | ||
? never | ||
: Key | ||
: Key]: Key extends keyof Filter | ||
? Filter[Key] extends DeepModify<Type[Key]> | ||
? DeepOmit<Type[Key], Filter[Key]> | ||
: Type[Key] | ||
: Type[Key]; | ||
: Key]: Key extends keyof Filter ? DeepOmit<Type[Key], Filter[Key]> : Type[Key]; | ||
} | ||
: never; |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,66 +1,47 @@ | ||
import { AnyRecord } from "../any-record"; | ||
import { Builtin } from "../built-in"; | ||
import { DeepModify } from "../deep-modify"; | ||
|
||
export type DeepPick<Type, Filter extends DeepModify<Type>> = Type extends Builtin | ||
export type DeepPick<Type, Filter> = Type extends Builtin | ||
? Type | ||
: Type extends Map<infer Keys, infer Values> | ||
? Filter extends Map<Keys, infer FilterValues> | ||
? FilterValues extends DeepModify<Values> | ||
? Map<Keys, DeepPick<Values, FilterValues>> | ||
: Type | ||
? Map<Keys, DeepPick<Values, FilterValues>> | ||
: Type | ||
: Type extends ReadonlyMap<infer Keys, infer Values> | ||
? Filter extends ReadonlyMap<Keys, infer FilterValues> | ||
? FilterValues extends DeepModify<Values> | ||
? ReadonlyMap<Keys, DeepPick<Values, FilterValues>> | ||
: Type | ||
? ReadonlyMap<Keys, DeepPick<Values, FilterValues>> | ||
: Type | ||
: Type extends WeakMap<infer Keys, infer Values> | ||
? Filter extends WeakMap<Keys, infer FilterValues> | ||
? FilterValues extends DeepModify<Values> | ||
? WeakMap<Keys, DeepPick<Values, FilterValues>> | ||
: Type | ||
? WeakMap<Keys, DeepPick<Values, FilterValues>> | ||
: Type | ||
: Type extends Set<infer Values> | ||
? Filter extends Set<infer FilterValues> | ||
? FilterValues extends DeepModify<Values> | ||
? Set<DeepPick<Values, FilterValues>> | ||
: Type | ||
? Set<DeepPick<Values, FilterValues>> | ||
: Type | ||
: Type extends ReadonlySet<infer Values> | ||
? Filter extends ReadonlySet<infer FilterValues> | ||
? FilterValues extends DeepModify<Values> | ||
? ReadonlySet<DeepPick<Values, FilterValues>> | ||
: Type | ||
? ReadonlySet<DeepPick<Values, FilterValues>> | ||
: Type | ||
: Type extends WeakSet<infer Values> | ||
? Filter extends WeakSet<infer FilterValues> | ||
? FilterValues extends DeepModify<Values> | ||
? WeakSet<DeepPick<Values, FilterValues>> | ||
: Type | ||
? WeakSet<DeepPick<Values, FilterValues>> | ||
: Type | ||
: Type extends Array<infer Values> | ||
? Filter extends Array<infer FilterValues> | ||
? FilterValues extends DeepModify<Values> | ||
? Array<DeepPick<Values, FilterValues>> | ||
: Type | ||
? Array<DeepPick<Values, FilterValues>> | ||
: Type | ||
: Type extends Promise<infer Value> | ||
? Filter extends Promise<infer FilterValue> | ||
? FilterValue extends DeepModify<Value> | ||
? Promise<DeepPick<Value, FilterValue>> | ||
: Type | ||
? Promise<DeepPick<Value, FilterValue>> | ||
: Type | ||
: Filter extends AnyRecord | ||
? { | ||
// iterate over keys of Type, which keeps the information about keys: optional, required or readonly | ||
[Key in keyof Type as Key extends keyof Filter ? Key : never]: Filter[Key & keyof Filter] extends true | ||
? Type[Key] | ||
: Key extends keyof Filter | ||
? Filter[Key] extends DeepModify<Type[Key]> | ||
? DeepPick<Type[Key], Filter[Key]> | ||
: never | ||
? DeepPick<Type[Key], Filter[Key]> | ||
: never; | ||
} | ||
: never; |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
`StrictDeepOmit<Type, Filter>` constructs a type by picking all properties from type `Type` and removing properties | ||
which values are `never` or `true` in type `Filter` | ||
|
||
```ts | ||
interface EnglishClass { | ||
students: { name: string; score: number }[]; | ||
teacher: { | ||
name: string; | ||
yearsOfExperience: number; | ||
}; | ||
year: number; | ||
} | ||
|
||
type AnonymousEnglishClass = StrictDeepOmit< | ||
// ^? { students: { score: number }[]; teacher: { yearsOfExperience: number }; year: number } | ||
EnglishClass, | ||
{ | ||
students: { name: never }[]; | ||
teacher: { | ||
name: never; | ||
}; | ||
} | ||
>; | ||
``` | ||
|
||
It validates that all `Filter` properties exist in type `Type` | ||
|
||
```ts | ||
// error: Type '{ id: never; students: { id: never; }[]; teacher: { id: never; }; }' does not satisfy the constraint '{ students?: true | DeepModify<{ name: string; score: number; }[]> | undefined; teacher?: true | { name?: true | undefined; yearsOfExperience?: true | undefined; } | undefined; year?: true | undefined; }'. | ||
// Types of property 'students' are incompatible. | ||
// Type '{ id: never; }[]' is not assignable to type 'true | DeepModify<{ name: string; score: number; }[]> | undefined'. | ||
// Type '{ id: never; }[]' is not assignable to type '(true | { name?: true | undefined; score?: true | undefined; } | undefined)[]'. | ||
// Type '{ id: never; }' is not assignable to type 'true | { name?: true | undefined; score?: true | undefined; } | undefined'. | ||
type UnknownEnglishClass = StrictDeepOmit< | ||
EnglishClass, | ||
{ | ||
id: never; | ||
students: { id: never }[]; | ||
teacher: { | ||
id: never; | ||
}; | ||
} | ||
// ^^^^^^^^^^^^^^^^^^^^^^^^ | ||
>; | ||
``` | ||
|
||
If you don't need a second type parameter `Filter` to be validated against a structure of a first type parameter `Type`, | ||
please use [`DeepOmit<Type, Filter>`](../deep-omit/). | ||
|
||
Useful in functions which cannot access specified properties | ||
|
||
```ts | ||
declare const englishClass: EnglishClass; | ||
|
||
const takeSurvey = (englishClass: AnonymousEnglishClass) => { | ||
// Property 'name' does not exist on type '{ yearsOfExperience: number; }' | ||
englishClass.teacher.name; | ||
// ^^^^ | ||
// Property 'name' does not exist on type '{ score: number; }' | ||
englishClass.students[0].name; | ||
// ^^^^ | ||
}; | ||
|
||
takeSurvey(englishClass); | ||
``` | ||
|
||
β οΈ Limitations: | ||
|
||
- `StrictDeepOmit` cannot be used when `Type` is generic type | ||
βΒ https://github.com/ts-essentials/ts-essentials/issues/343 (please use `DeepOmit` instead) | ||
- (bug) `StrictDeepOmit` cannot be used on nullable type - https://github.com/ts-essentials/ts-essentials/issues/380 | ||
- `StrictDeepOmit` only limits access to specified properties in your codebase, but doesn't remove them in runtime | ||
|
||
TS Playground βΒ https://tsplay.dev/wjo7lN |
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,4 @@ | ||
import { DeepModify } from "../deep-modify"; | ||
import { DeepOmit } from "../deep-omit"; | ||
|
||
export type StrictDeepOmit<Type, Filter extends DeepModify<Type>> = DeepOmit<Type, Filter>; |
Oops, something went wrong.