Skip to content

Commit

Permalink
PartialDeep: Add allowUndefinedInArrays option (#1019)
Browse files Browse the repository at this point in the history
Co-authored-by: Som Shekhar Mukherjee <[email protected]>
Co-authored-by: Sindre Sorhus <[email protected]>
  • Loading branch information
3 people authored Jan 4, 2025
1 parent a366c0f commit 278df80
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 10 deletions.
48 changes: 38 additions & 10 deletions source/partial-deep.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type {BuiltIns} from './internal';

/**
@see PartialDeep
@see {@link PartialDeep}
*/
export type PartialDeepOptions = {
/**
Expand All @@ -10,6 +10,32 @@ export type PartialDeepOptions = {
@default false
*/
readonly recurseIntoArrays?: boolean;

/**
Allows `undefined` values in non-tuple arrays.
- When set to `true`, elements of non-tuple arrays can be `undefined`.
- When set to `false`, only explicitly defined elements are allowed in non-tuple arrays, ensuring stricter type checking.
@default true
@example
You can prevent `undefined` values in non-tuple arrays by passing `{recurseIntoArrays: true; allowUndefinedInNonTupleArrays: false}` as the second type argument:
```
import type {PartialDeep} from 'type-fest';
type Settings = {
languages: string[];
};
declare const partialSettings: PartialDeep<Settings, {recurseIntoArrays: true; allowUndefinedInNonTupleArrays: false}>;
partialSettings.languages = [undefined]; // Error
partialSettings.languages = []; // Ok
```
*/
readonly allowUndefinedInNonTupleArrays?: boolean;
};

/**
Expand All @@ -25,12 +51,12 @@ import type {PartialDeep} from 'type-fest';
const settings: Settings = {
textEditor: {
fontSize: 14;
fontColor: '#000000';
fontWeight: 400;
}
autocomplete: false;
autosave: true;
fontSize: 14,
fontColor: '#000000',
fontWeight: 400
},
autocomplete: false,
autosave: true
};
const applySavedSettings = (savedSettings: PartialDeep<Settings>) => {
Expand All @@ -45,7 +71,7 @@ By default, this does not affect elements in array and tuple types. You can chan
```
import type {PartialDeep} from 'type-fest';
interface Settings {
type Settings = {
languages: string[];
}
Expand All @@ -54,6 +80,8 @@ const partialSettings: PartialDeep<Settings, {recurseIntoArrays: true}> = {
};
```
@see {@link PartialDeepOptions}
@category Object
@category Array
@category Set
Expand All @@ -74,8 +102,8 @@ export type PartialDeep<T, Options extends PartialDeepOptions = {}> = T extends
? Options['recurseIntoArrays'] extends true
? ItemType[] extends T // Test for arrays (non-tuples) specifically
? readonly ItemType[] extends T // Differentiate readonly and mutable arrays
? ReadonlyArray<PartialDeep<ItemType | undefined, Options>>
: Array<PartialDeep<ItemType | undefined, Options>>
? ReadonlyArray<PartialDeep<Options['allowUndefinedInNonTupleArrays'] extends false ? ItemType : ItemType | undefined, Options>>
: Array<PartialDeep<Options['allowUndefinedInNonTupleArrays'] extends false ? ItemType : ItemType | undefined, Options>>
: PartialObjectDeep<T, Options> // Tuples behave properly
: T // If they don't opt into array testing, just use the original type
: PartialObjectDeep<T, Options>
Expand Down
8 changes: 8 additions & 0 deletions test-d/partial-deep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,14 @@ expectAssignable<PartialDeep<RecurseObject>>(recurseObject);
const partialDeepNoRecurseIntoArraysFoo: PartialDeep<typeof foo> = foo;
// Check that `{recurseIntoArrays: true}` behaves as intended
expectType<PartialDeep<typeof foo, {recurseIntoArrays: true}>>(partialDeepFoo);

// Check that `{allowUndefinedInNonTupleArrays: true}` is the default
const partialDeepAllowUndefinedInNonTupleArraysFoo: PartialDeep<typeof foo, {recurseIntoArrays: true}> = foo;
expectType<Array<string | undefined> | undefined>(partialDeepAllowUndefinedInNonTupleArraysFoo.bar!.array);
// Check that `{allowUndefinedInNonTupleArrays: false}` behaves as intended
const partialDeepDoNotAllowUndefinedInNonTupleArraysFoo: PartialDeep<typeof foo, {recurseIntoArrays: true; allowUndefinedInNonTupleArrays: false}> = foo;
expectType<string[] | undefined>(partialDeepDoNotAllowUndefinedInNonTupleArraysFoo.bar!.array);

// These are mostly the same checks as before, but the array/tuple types are different.
// @ts-expect-error
expectType<Partial<typeof foo>>(partialDeepNoRecurseIntoArraysFoo);
Expand Down

0 comments on commit 278df80

Please sign in to comment.