Skip to content

Commit

Permalink
feat: βž• add StrictExclude (#297)
Browse files Browse the repository at this point in the history
* feat: βž• add StrictExclude

* docs: πŸ“„ README

* docs: πŸ“„ changeset

* test: πŸ§ͺ move StrictExtract to separate file

* test: πŸ§ͺ StrictExclude
  • Loading branch information
Beraliv committed Jan 31, 2022
1 parent e76a08a commit fa03dda
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 43 deletions.
5 changes: 5 additions & 0 deletions .changeset/five-gorillas-drop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"ts-essentials": minor
---

Add `StrictExclude`, a stricter version of `Exclude`
37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ If you use any [functions](https://github.com/krzkaczor/ts-essentials/blob/maste
- [Comparison between `Omit` and `StrictOmit`](#Comparison-between-Omit-and-StrictOmit)
- [StrictExtract](#StrictExtract)
- [Comparison between `Extract` and `StrictExtract`](#Comparison-between-Extract-and-StrictExtract)
- [StrictExclude](#StrictExclude)
- [Comparison between `Exclude` and `StrictExclude`](#Comparison-between-Exclude-and-StrictExclude)
- [DeepOmit](#DeepOmit)
- [DeepPick](#DeepPick)
- [OmitProperties](#OmitProperties)
Expand Down Expand Up @@ -462,6 +464,41 @@ type HouseAnimalWithExtract = Extract<Animal, { type: "dog" | "cat" | "horse" }>
// Result: no error
```

### StrictExclude

Usage is similar to the builtin version, but checks the filter type more strictly.

```typescript
type Animal = "dog" | "cat" | "mouse";

type DogAnimal = StrictExclude<Animal, "dog">;

// Result:
// 'cat' | 'mouse'

// if you want to Exclude multiple properties just use union type:
type HouseAnimal = StrictExclude<Animal, "dog" | "cat">;

// Result:
// 'mouse'
```

#### Comparison between `Exclude` and `StrictExclude`

Following the code above, we can compare the behavior of `Exclude` and `StrictExclude`.

```typescript
type HouseAnimalWithStrictExclude = StrictExclude<Animal, "dog" | "cat" | "horse">;

// Result: error
// Type '"dog" | "cat" | "horse"' is not assignable to type '"dog" | "cat" | "mouse"'
// Type '"horse"' is not assignable to type '"dog" | "cat" | "mouse"'.

type HouseAnimalWithExclude = Exclude<Animal, "dog" | "cat" | "horse">;

// Result: no error
```

### DeepOmit

Recursively omit deep properties according to key names.
Expand Down
3 changes: 3 additions & 0 deletions lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,9 @@ export type StrictOmit<T extends AnyRecord, K extends keyof T> = T extends AnyAr
/** Similar to the builtin Extract, but checks the filter strictly */
export type StrictExtract<T, U extends Partial<T>> = Extract<T, U>;

/** Similar to the builtin Exclude, but checks the filter strictly */
export type StrictExclude<T, U extends T> = Exclude<T, U>;

type PickKeysByValue<T, V> = { [K in keyof T]: T[K] extends V ? K : never }[keyof T];

/** Omit all properties of given type in object type */
Expand Down
42 changes: 0 additions & 42 deletions test/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -678,48 +678,6 @@ function testBuildable() {
];
}

function testStrictExtract() {
interface Dog {
type: "dog";
woof(): void;
}

interface Cat {
type: "cat";
meow(): void;
}

interface Mouse {
type: "mouse";
squeak(): void;
}

type Animal = Dog | Cat | Mouse;

type cases = [
// @ts-expect-error
StrictExtract<Animal, undefined>,
Assert<IsExact<StrictExtract<Animal, never>, never>>,
Assert<IsExact<StrictExtract<Animal, { type: undefined }>, never>>,
Assert<IsExact<StrictExtract<Animal, { type: never }>, never>>,
// @ts-expect-error
StrictExtract<Animal, "dog">,
Assert<IsExact<StrictExtract<Animal, { type: "dog" }>, Dog>>,
// @ts-expect-error
StrictExtract<Animal, "cat">,
Assert<IsExact<StrictExtract<Animal, { type: "cat" }>, Cat>>,
// @ts-expect-error
StrictExtract<Animal, "mouse">,
Assert<IsExact<StrictExtract<Animal, { type: "mouse" }>, Mouse>>,
// @ts-expect-error
StrictExtract<Animal, "cat" | "dog">,
Assert<IsExact<StrictExtract<Animal, { type: "cat" | "dog" }>, Cat | Dog>>,
// @ts-expect-error
StrictExtract<Animal, "cat" | "dog" | "mouse">,
Assert<IsExact<StrictExtract<Animal, { type: "cat" | "dog" | "mouse" }>, Animal>>,
];
}

function testOmitProperties() {
type cases = [
Assert<IsExact<OmitProperties<{}, never>, {}>>,
Expand Down
17 changes: 17 additions & 0 deletions test/strict-exclude.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { AssertTrue as Assert, IsExact } from "conditional-type-checks";
import { StrictExclude } from "../lib";

function testStrictExclude() {
type Animal = "dog" | "cat" | "mouse";

type cases = [
// @ts-expect-error
StrictExclude<Animal, undefined>,
Assert<IsExact<StrictExclude<Animal, never>, Animal>>,
Assert<IsExact<StrictExclude<Animal, "dog">, "cat" | "mouse">>,
Assert<IsExact<StrictExclude<Animal, "cat">, "dog" | "mouse">>,
Assert<IsExact<StrictExclude<Animal, "mouse">, "dog" | "cat">>,
Assert<IsExact<StrictExclude<Animal, "cat" | "dog">, "mouse">>,
Assert<IsExact<StrictExclude<Animal, "cat" | "dog" | "mouse">, never>>,
];
}
44 changes: 44 additions & 0 deletions test/strict-extract.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { AssertTrue as Assert, IsExact } from "conditional-type-checks";
import { StrictExtract } from "../lib";

function testStrictExtract() {
interface Dog {
type: "dog";
woof(): void;
}

interface Cat {
type: "cat";
meow(): void;
}

interface Mouse {
type: "mouse";
squeak(): void;
}

type Animal = Dog | Cat | Mouse;

type cases = [
// @ts-expect-error
StrictExtract<Animal, undefined>,
Assert<IsExact<StrictExtract<Animal, never>, never>>,
Assert<IsExact<StrictExtract<Animal, { type: undefined }>, never>>,
Assert<IsExact<StrictExtract<Animal, { type: never }>, never>>,
// @ts-expect-error
StrictExtract<Animal, "dog">,
Assert<IsExact<StrictExtract<Animal, { type: "dog" }>, Dog>>,
// @ts-expect-error
StrictExtract<Animal, "cat">,
Assert<IsExact<StrictExtract<Animal, { type: "cat" }>, Cat>>,
// @ts-expect-error
StrictExtract<Animal, "mouse">,
Assert<IsExact<StrictExtract<Animal, { type: "mouse" }>, Mouse>>,
// @ts-expect-error
StrictExtract<Animal, "cat" | "dog">,
Assert<IsExact<StrictExtract<Animal, { type: "cat" | "dog" }>, Cat | Dog>>,
// @ts-expect-error
StrictExtract<Animal, "cat" | "dog" | "mouse">,
Assert<IsExact<StrictExtract<Animal, { type: "cat" | "dog" | "mouse" }>, Animal>>,
];
}
1 change: 0 additions & 1 deletion test/strict-omit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ function testStrictOmit() {
Assert<IsExact<StrictOmit<{ a?: 1 }, never>, { a?: 1 }>>,
Assert<IsExact<StrictOmit<{ a: 1 }, "a">, {}>>,
Assert<IsExact<StrictOmit<{ a?: 1 }, "a">, {}>>,
Assert<IsExact<StrictOmit<{ a?: 1 }, "a">, {}>>,
// we don't prohibit arrays and tuples, but return never for them
Assert<IsExact<StrictOmit<readonly [], never>, never>>,
Assert<IsExact<StrictOmit<readonly [1, 2, 3], never>, never>>,
Expand Down

0 comments on commit fa03dda

Please sign in to comment.