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

Enhance Except type #560

Merged
merged 28 commits into from Mar 23, 2023
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
5b3138b
add StrictExcept type
orimiles5 Feb 26, 2023
fd7733a
prettier
orimiles5 Feb 26, 2023
29ce64d
added example for StrictExcept
orimiles5 Feb 26, 2023
03efc42
create Except type with optional strict
orimiles5 Feb 28, 2023
a0636bb
change name to
orimiles5 Feb 28, 2023
8bcc45c
change to single quotes
orimiles5 Feb 28, 2023
3c5e005
make StrictExcept false by default
orimiles5 Mar 1, 2023
0b5573b
remove test for strict-except
orimiles5 Mar 1, 2023
3ef2af0
Update except.d.ts
sindresorhus Mar 10, 2023
f14a038
Update except.d.ts
sindresorhus Mar 10, 2023
d7a1d0f
Add `IsLiteral` types (#563)
tommy-mitchell Mar 10, 2023
dfb46ad
Add `AbstractClass` type (#559)
rayrw Mar 11, 2023
7aac550
fix import statment and remove extra types
orimiles5 Mar 11, 2023
3b30962
set tsd to ignore noUnusedLocals error by default
orimiles5 Mar 11, 2023
911352c
add test cases for strict except
orimiles5 Mar 11, 2023
3432e46
Merge branch 'sindresorhus:main' into dev
orimiles5 Mar 11, 2023
5de63de
Update except.d.ts
sindresorhus Mar 14, 2023
e2a2cc0
Merge branch 'main' into dev
sindresorhus Mar 14, 2023
9d63b07
change strict option name to be more specific and document the trade-off
orimiles5 Mar 14, 2023
730d469
Fix documentation
orimiles5 Mar 14, 2023
6ffbed6
Update Except type documentation
orimiles5 Mar 14, 2023
880e7e4
Merge branch 'main' into 'dev'
orimiles5 Mar 22, 2023
87fcc50
Modify Except type docs
orimiles5 Mar 22, 2023
af2e28b
Modify ExceptOptions docs
orimiles5 Mar 22, 2023
f8e36b3
Update readme.md
sindresorhus Mar 23, 2023
f5d2a10
Update except.d.ts
sindresorhus Mar 23, 2023
0a15a94
Update except.d.ts
sindresorhus Mar 23, 2023
fa7c464
Merge branch 'main' into dev
sindresorhus Mar 23, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions package.json
Expand Up @@ -49,5 +49,10 @@
"@typescript-eslint/no-redeclare": "off",
"@typescript-eslint/no-confusing-void-expression": "off"
}
},
"tsd": {
"compilerOptions": {
"noUnusedLocals": false
}
}
}
5 changes: 3 additions & 2 deletions readme.md
Expand Up @@ -129,7 +129,8 @@ Click the type names for complete docs.

- [`EmptyObject`](source/empty-object.d.ts) - Represents a strictly empty plain object, the `{}` value.
- [`IsEmptyObject`](source/empty-object.d.ts) - Returns a `boolean` for whether the type is strictly equal to an empty plain object, the `{}` value.
- [`Except`](source/except.d.ts) - Create a type from an object type without certain keys. This is a stricter version of [`Omit`](https://www.typescriptlang.org/docs/handbook/utility-types.html#omittype-keys).
- [`Except`](source/except.d.ts) - Create a type from an object type without certain keys. Use option `requireExactProps: true` to disallow assigning additional properties to this type. (Note that that any omitted properties in the resulting type will be present in suggestions as `undefined`.)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The description here should not be changed. The option is explained in the type docs. That's enough.

This is a stricter version of [`Omit`](https://www.typescriptlang.org/docs/handbook/utility-types.html#omittype-keys).
- [`Writable`](source/writable.d.ts) - Create a type that strips `readonly` from all or some of an object's keys. The inverse of `Readonly<T>`.
- [`WritableDeep`](source/writable-deep.d.ts) - Create a deeply mutable version of an `object`/`ReadonlyMap`/`ReadonlySet`/`ReadonlyArray` type. The inverse of `ReadonlyDeep<T>`. Use `Writable<T>` if you only need one level deep.
- [`Merge`](source/merge.d.ts) - Merge two types into a new type. Keys of the second type overrides keys of the first type.
Expand Down Expand Up @@ -910,4 +911,4 @@ You can find some examples in the [TypeScript docs](https://www.typescriptlang.o

## License

SPDX-License-Identifier: (MIT OR CC0-1.0)
SPDX-License-Identifier: (MIT OR CC0-1.0)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't make unrelated changes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't figure out what was the change, maybe it's a white space?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the trailing new line was changed.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@orimiles5 This still needs to be fixed.

Copy link
Contributor Author

@orimiles5 orimiles5 Mar 22, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sindresorhus Could you give me some advice on how to fix this?

33 changes: 27 additions & 6 deletions source/except.d.ts
Expand Up @@ -29,8 +29,19 @@ type Filtered = Filter<'bar', 'foo'>;
*/
type Filter<KeyType, ExcludeType> = IsEqual<KeyType, ExcludeType> extends true ? never : (KeyType extends ExcludeType ? never : KeyType);

type ExceptOptions = {
/**
Disallow assigning non-specified properties.

Setting this to `false` is not recommended. Included for backwards-compatability.

@default false
*/
requireExactProps?: boolean;
};

/**
Create a type from an object type without certain keys.
Create a type from an object type without certain keys. Use option `requireExactProps: true` to disallow assigning additional properties to this type. Note that any omitted properties in the resulting type will be present in suggestions as `undefined`.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This text should be in the docs for ExceptOptions instead. The only text we need here is just:

We recommend setting the requireExactProps option to true.


This type is a stricter version of [`Omit`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-5.html#the-omit-helper-type). The `Omit` type does not restrict the omitted keys to be keys present on the given type, while `Except` does. The benefits of a stricter type are avoiding typos and allowing the compiler to pick up on rename refactors automatically.

Expand All @@ -43,15 +54,25 @@ import type {Except} from 'type-fest';
type Foo = {
a: number;
b: string;
c: boolean;
};

type FooWithoutA = Except<Foo, 'a' | 'c'>;
//=> {b: string};
type FooWithoutA = Except<Foo, 'a'>;
//=> {b: string}

const fooWithoutA: FooWithoutA = {a: 1, b: '2'};
//=> errors: 'a' does not exist in type '{ b: string; }'

type FooWithoutB = Except<Foo, 'b', {requireExactProps: true}>;
//=> {a: number} & Partial<Record<"b", never>>

const fooWithoutB: FooWithoutB = {a: 1, b: '2'};
//=> errors at 'b': Type 'string' is not assignable to type 'undefined'.
```

@category Object
*/
export type Except<ObjectType, KeysType extends keyof ObjectType> = {
export type Except<ObjectType, KeysType extends keyof ObjectType, Options extends ExceptOptions = {requireExactProps: false}> = {
[KeyType in keyof ObjectType as Filter<KeyType, KeysType>]: ObjectType[KeyType];
};
} & (Options['requireExactProps'] extends true
? Partial<Record<KeysType, never>>
: {});
18 changes: 16 additions & 2 deletions test-d/except.ts
@@ -1,8 +1,22 @@
import {expectType} from 'tsd';
import {expectType, expectError} from 'tsd';
import type {Except} from '../index';

declare const except: Except<{a: number; b: string}, 'b'>;
expectType<{a: number}>(except);
expectError(except.b);

const nonStrict = {
a: 1,
b: '2',
};

const nonStrictAssignment: typeof except = nonStrict; // No error

declare const strictExcept: Except<{a: number; b: string}, 'b', {requireExactProps: true}>;

expectError(() => {
const strictAssignment: typeof strictExcept = nonStrict;
});

// Generic properties
type Example = {
Expand All @@ -11,7 +25,7 @@ type Example = {
bar: string;
sindresorhus marked this conversation as resolved.
Show resolved Hide resolved
};

const test: Except<Example, 'bar'> = {foo: 123, bar: 'asdf'};
const test: Except<Example, 'bar', {requireExactProps: false}> = {foo: 123, bar: 'asdf'};
expectType<number>(test.foo);
// eslint-disable-next-line @typescript-eslint/dot-notation
expectType<unknown>(test['bar']);