PartialDeep: Add allowUndefinedInArrays option (#1019)

Co-authored-by: Som Shekhar Mukherjee <49264891+som-sm@users.noreply.github.com>
Co-authored-by: Sindre Sorhus <sindresorhus@gmail.com>
This commit is contained in:
Pico12 2025-01-04 18:03:47 +01:00 committed by GitHub
parent a366c0f821
commit 278df8055d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 46 additions and 10 deletions

View File

@ -1,7 +1,7 @@
import type {BuiltIns} from './internal';
/**
@see PartialDeep
@see {@link PartialDeep}
*/
export type PartialDeepOptions = {
/**
@ -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;
};
/**
@ -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>) => {
@ -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[];
}
@ -54,6 +80,8 @@ const partialSettings: PartialDeep<Settings, {recurseIntoArrays: true}> = {
};
```
@see {@link PartialDeepOptions}
@category Object
@category Array
@category Set
@ -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>

View File

@ -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);