Skip to content
Open
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
1 change: 1 addition & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export type {SetReadonly} from './source/set-readonly.d.ts';
export type {SetRequired} from './source/set-required.d.ts';
export type {SetRequiredDeep} from './source/set-required-deep.d.ts';
export type {SetNonNullable} from './source/set-non-nullable.d.ts';
export type {SetNullable} from './source/set-nullable.d.ts';
export type {SetNonNullableDeep} from './source/set-non-nullable-deep.d.ts';
export type {ValueOf} from './source/value-of.d.ts';
export type {AsyncReturnType} from './source/async-return-type.d.ts';
Expand Down
1 change: 1 addition & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ Click the type names for complete docs.
- [`SetReadonly`](source/set-readonly.d.ts) - Create a type that makes the given keys readonly.
- [`SetRequired`](source/set-required.d.ts) - Create a type that makes the given keys required.
- [`SetRequiredDeep`](source/set-required-deep.d.ts) - Like `SetRequired` except it selects the keys deeply.
- [`SetNullable`](source/set-nullable.d.ts) - Create a type that makes the given keys nullable.
- [`SetNonNullable`](source/set-non-nullable.d.ts) - Create a type that makes the given keys non-nullable.
- [`SetNonNullableDeep`](source/set-non-nullable-deep.d.ts) - Create a type that makes the specified keys non-nullable (removes `null` and `undefined`), supports deeply nested key paths, and leaves all other keys unchanged.
- [`ValueOf`](source/value-of.d.ts) - Create a union of the given object's values, and optionally specify which keys to get the values from.
Expand Down
35 changes: 35 additions & 0 deletions source/set-nullable.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
Create a type that makes the given keys nullable, where the remaining keys are kept as is.

If no keys are given, all keys will be made nullable.

Use-case: You want to define a single model where the only thing that changes is whether or not some or all of the keys are nullable.

@example
```
import type {SetNullable} from 'type-fest';

type Foo = {
a: number;
b: string;
c?: boolean;
};

type SomeNullable = SetNullable<Foo, 'a' | 'c'>; // Optionality is preserved for `c`
//=> {a: number | null; b: string; c?: boolean | null}

type AllNullable = SetNullable<Foo>;
//=> {a: number | null; b: string | null; c?: boolean | null}
```

@see {@link SetNonNullable}

@category Object
*/
export type SetNullable<BaseType, Keys extends keyof BaseType = keyof BaseType> = {
[Key in keyof BaseType]: Key extends Keys
? BaseType[Key] | null
: BaseType[Key];
};

export {};
57 changes: 57 additions & 0 deletions test-d/set-nullable.ts
Comment thread
som-sm marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import {expectNotAssignable, expectType} from 'tsd';
import type {SetNullable} from '../index.d.ts';

// Make one key nullable.
declare const variation1: SetNullable<{a: number; b: string}, 'a'>;
expectType<{a: number | null; b: string}>(variation1);

// Make multiple keys nullable.
declare const variation2: SetNullable<{a: number; b: string; c: boolean}, 'a' | 'c'>;
expectType<{a: number | null; b: string; c: boolean | null}>(variation2);

// Preserve optional modifier.
declare const variation3: SetNullable<{a: number; b?: string}, 'b'>;
expectType<{a: number; b?: string | null}>(variation3);

// Key already nullable remains unchanged.
declare const variation4: SetNullable<{a: number | null; b: string}, 'a'>;
expectType<{a: number | null; b: string}>(variation4);

// Fail if type changes even if nullable is right.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

WDYM by this? What's failing here?

declare const variation5: SetNullable<{a: number; b: string}, 'b'>;
expectNotAssignable<{a: string; b: string | null}>(variation5);

// Make all keys nullable when `Keys` generic is not passed.
declare const variation6: SetNullable<{a: number; b: string; c: boolean}>;
expectType<{a: number | null; b: string | null; c: boolean | null}>(variation6);

// Preserve readonly modifier.
declare const variation7: SetNullable<{readonly a: number; b: string}, 'a'>;
expectType<{readonly a: number | null; b: string}>(variation7);

// Works with unions.
declare const variation8: SetNullable<{a: '1'; b: string; c: boolean} | {a: '2'; b: string; c: boolean}, 'a' | 'b'>;
expectType<{a: '1' | null; b: string | null; c: boolean} | {a: '2' | null; b: string | null; c: boolean}>(variation8);

// Works with index signatures.
declare const variation9: SetNullable<{[k: string]: unknown; a: number; b: string}, 'a'>;
expectType<{[k: string]: unknown; a: number | null; b: string}>(variation9);

declare const variation9a: SetNullable<{[k: Uppercase<string>]: number; A: number; b: string}, Uppercase<string>>;
expectType<{[k: Uppercase<string>]: number | null; A: number | null; b: string}>(variation9a);

// Makes all keys nullable when `Keys` is `any`.
declare const variation10: SetNullable<{readonly a: number; b?: string; c: boolean}, any>;
expectType<{readonly a: number | null; b?: string | null; c: boolean | null}>(variation10);

// Does nothing when `Keys` is `never`.
declare const variation11: SetNullable<{a: number; readonly b?: string; readonly c: boolean}, never>;
expectType<{a: number; readonly b?: string; readonly c: boolean}>(variation11);

Comment thread
som-sm marked this conversation as resolved.
// Preserves existing `undefined` when adding `null`.
declare const variation12: SetNullable<{a: string | undefined; b: number}, 'a'>;
expectType<{a: string | undefined | null; b: number}>(variation12);

// Works with unions where only some keys are shared across branches.
declare const variation13: SetNullable<{a: number; c: boolean} | {a: string; d: boolean}, 'a'>;
expectType<{a: number | null; c: boolean} | {a: string | null; d: boolean}>(variation13);