From eaee401b7cf6864616f3caa7a462419075640300 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=ED=95=B4=EC=A4=80?= <99087502+haejunejung@users.noreply.github.com> Date: Sun, 25 Aug 2024 12:00:13 +0900 Subject: [PATCH] feat(StrictExclude): Add `StrictExclude` (#40) * feat(StrictExclude): Add StrictExclude type * test(StrictExclude): Add StrictExclude type test * docs(StrictExclude): Add StrictExclude type documentation --- docs/.vitepress/en.mts | 4 ++ docs/.vitepress/ko.mts | 4 ++ docs/ko/reference/utilities/StrictExclude.md | 53 +++++++++++++++++++ docs/reference/utilities/StrictExclude.md | 54 ++++++++++++++++++++ source/utilities/StrictExclude.d.ts | 23 +++++++++ source/utilities/index.ts | 1 + test-d/utilities/StrictExclude.test-d.ts | 21 ++++++++ 7 files changed, 160 insertions(+) create mode 100644 docs/ko/reference/utilities/StrictExclude.md create mode 100644 docs/reference/utilities/StrictExclude.md create mode 100644 source/utilities/StrictExclude.d.ts create mode 100644 test-d/utilities/StrictExclude.test-d.ts diff --git a/docs/.vitepress/en.mts b/docs/.vitepress/en.mts index 7f8f760..9081396 100644 --- a/docs/.vitepress/en.mts +++ b/docs/.vitepress/en.mts @@ -70,6 +70,10 @@ export default defineConfig({ text: 'StrictOmit', link: '/reference/utilities/StrictOmit', }, + { + text: 'StrictExclude', + link: '/reference/utilities/StrictExclude', + }, ], }, { diff --git a/docs/.vitepress/ko.mts b/docs/.vitepress/ko.mts index 9a33d6d..1badae1 100644 --- a/docs/.vitepress/ko.mts +++ b/docs/.vitepress/ko.mts @@ -73,6 +73,10 @@ export default defineConfig({ text: 'StrictOmit', link: '/ko/reference/utilities/StrictOmit', }, + { + text: 'StrictExclude', + link: '/ko/reference/utilities/StrictExclude', + }, ], }, { diff --git a/docs/ko/reference/utilities/StrictExclude.md b/docs/ko/reference/utilities/StrictExclude.md new file mode 100644 index 0000000..1b6dda3 --- /dev/null +++ b/docs/ko/reference/utilities/StrictExclude.md @@ -0,0 +1,53 @@ +# StrictExclude\ + +## 개요 + +이 타입은 TypeScript의 `Exclude` 유틸리티 타입의 더 엄격한 버전이에요. + +`Exclude`는 주어진 타입에 없는 멤버도 제외할 수 있지만, `StrictExclude`는 오직 실제로 타입에 존재하는 멤버만 제외할 수 있도록 보장해요. + +## 문법 + +```ts +type StrictExclude = Exclude< + BaseType, + ExcludedMembers +>; +``` + +- **BaseType**: 유니온 멤버를 제외하고자 하는 기존 타입이에요. +- **ExcludedMembers**: `BaseType`에서 제외하려는 유니온 멤버 혹은 타입이에요. + +## 예제 + +#### 예제 #1 + +```ts +type Example = 'admin' | 'editor' | 'viewer'; +type StrictExcludedExample = StrictExclude; +// 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, + }, + ], + }, +}; +``` + +::: diff --git a/docs/reference/utilities/StrictExclude.md b/docs/reference/utilities/StrictExclude.md new file mode 100644 index 0000000..67400b1 --- /dev/null +++ b/docs/reference/utilities/StrictExclude.md @@ -0,0 +1,54 @@ +# StrictExclude\ + +## 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 = 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; +// 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, + }, + ], + }, +}; +``` + +::: diff --git a/source/utilities/StrictExclude.d.ts b/source/utilities/StrictExclude.d.ts new file mode 100644 index 0000000..f00abac --- /dev/null +++ b/source/utilities/StrictExclude.d.ts @@ -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 + * // Result: "editor" | "viewer" + */ + +export type StrictExclude = Exclude< + BaseType, + ExcludedMembers +>; diff --git a/source/utilities/index.ts b/source/utilities/index.ts index d68bbae..03a8ef9 100644 --- a/source/utilities/index.ts +++ b/source/utilities/index.ts @@ -1 +1,2 @@ +export type { StrictExclude } from './StrictExclude'; export type { StrictOmit } from './StrictOmit'; diff --git a/test-d/utilities/StrictExclude.test-d.ts b/test-d/utilities/StrictExclude.test-d.ts new file mode 100644 index 0000000..b8ddea1 --- /dev/null +++ b/test-d/utilities/StrictExclude.test-d.ts @@ -0,0 +1,21 @@ +import { StrictExclude } from '@/utilities'; +import { expectNever, expectType } from 'tsd'; + +declare function strictExclude< + BaseType, + ExcludedMembers extends BaseType, +>(): StrictExclude; + +// Should correctly handle basic case. +type Example0 = 'admin' | 'editor' | 'viewer'; +type Expected0 = 'admin' | 'editor'; +expectType(strictExclude()); + +// Should return the original type if excluding no members. +type Example1 = 'a' | 'b' | 'c'; +type Expected1 = 'a' | 'b' | 'c'; +expectType(strictExclude()); + +// Should return `never` if excluding all members; +type Example2 = 'a' | 'b' | 'c'; +expectNever(strictExclude());