Skip to content

Commit

Permalink
feat(StrictExclude): Add StrictExclude (#40)
Browse files Browse the repository at this point in the history
* feat(StrictExclude): Add StrictExclude type

* test(StrictExclude): Add StrictExclude type test

* docs(StrictExclude): Add StrictExclude type documentation
  • Loading branch information
haejunejung authored Aug 25, 2024
1 parent cc3d311 commit eaee401
Show file tree
Hide file tree
Showing 7 changed files with 160 additions and 0 deletions.
4 changes: 4 additions & 0 deletions docs/.vitepress/en.mts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ export default defineConfig({
text: 'StrictOmit',
link: '/reference/utilities/StrictOmit',
},
{
text: 'StrictExclude',
link: '/reference/utilities/StrictExclude',
},
],
},
{
Expand Down
4 changes: 4 additions & 0 deletions docs/.vitepress/ko.mts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ export default defineConfig({
text: 'StrictOmit',
link: '/ko/reference/utilities/StrictOmit',
},
{
text: 'StrictExclude',
link: '/ko/reference/utilities/StrictExclude',
},
],
},
{
Expand Down
53 changes: 53 additions & 0 deletions docs/ko/reference/utilities/StrictExclude.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# StrictExclude\<BaseType, ExcludedMembers>

## 개요

이 타입은 TypeScript의 `Exclude` 유틸리티 타입의 더 엄격한 버전이에요.

`Exclude`는 주어진 타입에 없는 멤버도 제외할 수 있지만, `StrictExclude`는 오직 실제로 타입에 존재하는 멤버만 제외할 수 있도록 보장해요.

## 문법

```ts
type StrictExclude<BaseType, ExcludedMembers extends BaseType> = Exclude<
BaseType,
ExcludedMembers
>;
```

- **BaseType**: 유니온 멤버를 제외하고자 하는 기존 타입이에요.
- **ExcludedMembers**: `BaseType`에서 제외하려는 유니온 멤버 혹은 타입이에요.

## 예제

#### 예제 #1

```ts
type Example = 'admin' | 'editor' | 'viewer';
type StrictExcludedExample = StrictExclude<Example, 'admin'>;
// Result: "editor" | "viewer"
```

:::tip
If your team is using ESLint and wants to enforce the use of `StrictExclude` instead of the standard `Exclude`, you can configure ESLint to help catch this. The `@typescript-eslint/ban-types` rule can be configured to display an error message when `Exclude` is used, guiding developers to use `StrictExclude` instead. Here's how you can set up your ESLint configuration:

```js
module.exports = {
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
rules: {
// Include other relevant rules here
'@typescript-eslint/ban-types': [
'error',
{
types: {
Exclude: 'Use StrictExclude instead',
},
extendsDefaults: true,
},
],
},
};
```

:::
54 changes: 54 additions & 0 deletions docs/reference/utilities/StrictExclude.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# StrictExclude\<BaseType, ExcludedMembers>

## Overview

This type is a stricter version of TypeScript's `Exclude` utility type.

Unlike `Exclude`, which allows excluded members not present on the given type,
`StrictExclude` ensures that only members actually present in the type can be excluded.

## Syntax

```ts
type StrictExclude<BaseType, ExcludedMembers extends BaseType> = Exclude<
BaseType,
ExcludedMembers
>;
```

- **BaseType**: The type from which you want to exclude union members.
- **ExcludedMembers**: The members or types that you want to exclude from `BaseType`.

## Examples

#### Example #1

```ts
type Example = 'admin' | 'editor' | 'viewer';
type StrictExcludedExample = StrictExclude<Example, 'admin'>;
// Result: "editor" | "viewer"
```

:::tip
If your team is using ESLint and wants to enforce the use of `StrictExclude` instead of the standard `Exclude`, you can configure ESLint to help catch this. The `@typescript-eslint/ban-types` rule can be configured to display an error message when `Exclude` is used, guiding developers to use `StrictExclude` instead. Here's how you can set up your ESLint configuration:

```js
module.exports = {
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
rules: {
// Include other relevant rules here
'@typescript-eslint/ban-types': [
'error',
{
types: {
Exclude: 'Use StrictExclude instead',
},
extendsDefaults: true,
},
],
},
};
```

:::
23 changes: 23 additions & 0 deletions source/utilities/StrictExclude.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* @description
* This type is a stricter version of TypeScript's `Exclude` utility type.
*
* Unlike `Exclude`, which allows excluded members not present on the given type,
* `StrictExclude` ensures that only members actually present in the type can be excluded.
*
* @template BaseType - The type from which you want to exclude union members.
* @template ExcludedMembers - The members or types that you want to exclude from `BaseType`.
*
* @returns
* A new type that consists of all memebers of `BaseType` except that are assignable to `ExcludedMembers`.
*
* @example
* type Example = "admin" | "editor" | "viewer";
* type StrictExcludedExample = StrictExclude<Example, "admin">
* // Result: "editor" | "viewer"
*/

export type StrictExclude<BaseType, ExcludedMembers extends BaseType> = Exclude<
BaseType,
ExcludedMembers
>;
1 change: 1 addition & 0 deletions source/utilities/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export type { StrictExclude } from './StrictExclude';
export type { StrictOmit } from './StrictOmit';
21 changes: 21 additions & 0 deletions test-d/utilities/StrictExclude.test-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { StrictExclude } from '@/utilities';
import { expectNever, expectType } from 'tsd';

declare function strictExclude<
BaseType,
ExcludedMembers extends BaseType,
>(): StrictExclude<BaseType, ExcludedMembers>;

// Should correctly handle basic case.
type Example0 = 'admin' | 'editor' | 'viewer';
type Expected0 = 'admin' | 'editor';
expectType<Expected0>(strictExclude<Example0, 'viewer'>());

// Should return the original type if excluding no members.
type Example1 = 'a' | 'b' | 'c';
type Expected1 = 'a' | 'b' | 'c';
expectType<Expected1>(strictExclude<Example1, never>());

// Should return `never` if excluding all members;
type Example2 = 'a' | 'b' | 'c';
expectNever(strictExclude<Example2, 'a' | 'b' | 'c'>());

0 comments on commit eaee401

Please sign in to comment.