Допустим, вы следуете правилам кодирования проекта TypeScript и используете только undefined
. Ваши типы определены с необязательными свойствами, не допускающими значения NULL (например, x?: number
), но данные, поступающие из API, вместо этого возвращают null
.
Вы решаете написать функцию для удаления всех этих null
значений из ответа, чтобы они соответствовали вашим типам:
function stripNullableProperties(obj) {
// Return a new object without null properties
}
Как можно строго набирать такой помощник, не дублируя типы ввода и вывода? Вы можете попробовать:
function stripNullableProperties<T extends {}>(obj: T): T;
Но он не будет работать в режиме строгой проверки нуля, поскольку obj
может иметь null
значения, которые нельзя присвоить необязательным свойствам, не допускающим значения NULL, в T
:
type A = {
x: number;
y?: number;
};
stripNullableProperties<A>({
x: 1,
y: null // Error: Type 'null' is not assignable to type 'number | undefined'.
});
Что вам действительно нужно, так это что-то вроде:
function stripNullableProperties<T extends {}>(obj: NullableOptional<T>): T;
Тип NullableOptional
‹T›
Тип NullableOptional<T>
создает тип со всеми необязательными свойствами T
, которые допускают значение NULL:
type A = {
x: number;
y?: number;
};
type B = NullableOptional<A>;
// {
// x: number;
// y?: number | null;
// }
Вы не найдете NullableOptional
в документации TypeScript, потому что это настраиваемый тип. На самом деле это выглядит так:
type RequiredKeys<T> = { [K in keyof T]-?: {} extends { [P in K]: T[K] } ? never : K }[keyof T];
type OptionalKeys<T> = { [K in keyof T]-?: {} extends { [P in K]: T[K] } ? K : never }[keyof T];
type PickRequired<T> = Pick<T, RequiredKeys<T>>;
type PickOptional<T> = Pick<T, OptionalKeys<T>>;
type Nullable<T> = { [P in keyof T]: T[P] | null };
type NullableOptional<T> = PickRequired<T> & Nullable<PickOptional<T>>;
Суммируя:
- выберите нужные свойства из
T
; - выберите необязательные свойства из
T
и сделайте их допускающими значение NULL; - пересекаются (1) с (2).
С его помощью вы можете удалить все свойства, допускающие значение NULL, из объекта, интерфейс которого должен иметь только необязательные свойства, не допускающие значения NULL, при этом обеспечивая безопасность типов:
type A = {
x: number;
y?: number;
};
stripNullableProperties<A>({
x: 1,
y: null
});
// {
// x: 1
// }: A
Вы всегда можете выполнить утверждение типа и избежать всех этих проблем, но обеспечение безопасности типов - даже в таких, казалось бы, безвредных случаях, подобных этому, - окупается в долгосрочной перспективе.
Первоначально опубликовано на rbardini.com 20 декабря 2019 г.