Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(StrictExclude): Add StrictExclude #40

Merged
merged 3 commits into from
Aug 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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'>());
Loading