有没有办法在对象 属性 解构中利用无效合并运算符 (`??`)?
Is there a way to utilize the nullish coalescing operator (`??`) in object property destructuring?
在 ReactJS 中,我通常使用这种 destructurnig props 模式(我想这很地道):
export default function Example({ ExampleProps }) {
const {
content,
title,
date,
featuredImage,
author,
tags,
} = ExampleProps || {};
我可以在解构时添加默认值,这增加了一些安全性:
export default function Example({ ExampleProps }) {
const {
content = "",
title = "Missing Title",
date = "",
featuredImage = {},
author = {},
tags = [],
} = ExampleProps || {};
但现在我切换到 TypeScript strict
模式,我很难过。我的道具由 GraphQl codegen 键入,几乎所有属性都包装在 Maybe<T>
类型中,所以当展开时,有像 actualValue | null | undefined
.
如果值为 undefined
,默认值 ({ maybeUndefined = ""} = props)
可以拯救我,但 null
值会落空 ,所以TS 编译器很烦人,我的代码导致了很多:
tags?.nodes?.length // etc…
这让我有点紧张,因为 The Costs of Optional Chaining 文章(尽管我不知道它在 2021 年的相关性如何)。我还听说 ?.
运算符过度使用被称为“代码味道”的一个例子。
是否有一种模式(可能利用 ??
运算符)可以使 TS 编译器满意并且至少可以清除其中的一些 very?.long?.optional?.chains
?
我看到两个可能的选择:
执行无效合并 属性-by-属性,或
使用效用函数
属性 来自 属性
相当乏味(我是一个乏味的开发者):
// Default `ExampleProps` here −−−−−−−−−−−−−−−vvvvv
export default function Example({ ExampleProps = {} }) {
// Then do the nullish coalescing per-item
const content = ExampleProps.content ?? "";
const title = ExampleProps.title ?? "Missing Title";
const date = ExampleProps.date ?? "";
const featuredImage = ExampleProps.featuredImage ?? {},
const author = ExampleProps.author ?? {},
const tags = ExampleProps.tags ?? [];
// ...
效用函数
或者,沿着这些行使用实用函数将 null
值(编译时和运行时)转换为 undefined
,因此您可以在解构结果时使用解构默认值。类型部分相当简单:
type NullToUndefined<Type> = {
[key in keyof Type]: Exclude<Type[key], null>;
}
那么效用函数可能是这样的:
function nullToUndefined<
SourceType extends object,
ResultType = NullToUndefined<SourceType>
>(object: SourceType) {
return Object.fromEntries(
Object.entries(object).map(([key, value]) => [key, value ?? undefined])
) as ResultType;
}
或像这样(在运行时方面可能更有效):
function nullToUndefined<
SourceType extends object,
ResultType = NullToUndefined<SourceType>
>(object: SourceType) {
const source = object as {[key: string]: any};
const result: {[key: string]: any} = {};
for (const key in object) {
if (Object.hasOwn(object, key)) {
result[key] = source[key] ?? undefined;
}
}
return result as ResultType;
}
请注意 Object.hasOwn
非常 新,但很容易填充。或者您可以改用 Object.prototype.hasOwn.call(object, key)
。
(在这两种情况下 within nullToUndefined
我在类型断言方面玩得有点快和松散。对于像这样的小实用函数,我认为这是一个合理的妥协,前提是输入和输出定义明确。)
然后:
export default function Example({ ExampleProps }) {
const {
content = "",
title = "Missing Title",
date = "",
featuredImage = {},
author = {},
tags = [],
} = nullToUndefined(ExampleProps || {});
// ^^^^^^^^^^^^^^^^−−−−−−−−−−−−−−−−−−^
// ...
在 ReactJS 中,我通常使用这种 destructurnig props 模式(我想这很地道):
export default function Example({ ExampleProps }) {
const {
content,
title,
date,
featuredImage,
author,
tags,
} = ExampleProps || {};
我可以在解构时添加默认值,这增加了一些安全性:
export default function Example({ ExampleProps }) {
const {
content = "",
title = "Missing Title",
date = "",
featuredImage = {},
author = {},
tags = [],
} = ExampleProps || {};
但现在我切换到 TypeScript strict
模式,我很难过。我的道具由 GraphQl codegen 键入,几乎所有属性都包装在 Maybe<T>
类型中,所以当展开时,有像 actualValue | null | undefined
.
如果值为 undefined
,默认值 ({ maybeUndefined = ""} = props)
可以拯救我,但 null
值会落空 ,所以TS 编译器很烦人,我的代码导致了很多:
tags?.nodes?.length // etc…
这让我有点紧张,因为 The Costs of Optional Chaining 文章(尽管我不知道它在 2021 年的相关性如何)。我还听说 ?.
运算符过度使用被称为“代码味道”的一个例子。
是否有一种模式(可能利用 ??
运算符)可以使 TS 编译器满意并且至少可以清除其中的一些 very?.long?.optional?.chains
?
我看到两个可能的选择:
执行无效合并 属性-by-属性,或
使用效用函数
属性 来自 属性
相当乏味(我是一个乏味的开发者):
// Default `ExampleProps` here −−−−−−−−−−−−−−−vvvvv
export default function Example({ ExampleProps = {} }) {
// Then do the nullish coalescing per-item
const content = ExampleProps.content ?? "";
const title = ExampleProps.title ?? "Missing Title";
const date = ExampleProps.date ?? "";
const featuredImage = ExampleProps.featuredImage ?? {},
const author = ExampleProps.author ?? {},
const tags = ExampleProps.tags ?? [];
// ...
效用函数
或者,沿着这些行使用实用函数将 null
值(编译时和运行时)转换为 undefined
,因此您可以在解构结果时使用解构默认值。类型部分相当简单:
type NullToUndefined<Type> = {
[key in keyof Type]: Exclude<Type[key], null>;
}
那么效用函数可能是这样的:
function nullToUndefined<
SourceType extends object,
ResultType = NullToUndefined<SourceType>
>(object: SourceType) {
return Object.fromEntries(
Object.entries(object).map(([key, value]) => [key, value ?? undefined])
) as ResultType;
}
或像这样(在运行时方面可能更有效):
function nullToUndefined<
SourceType extends object,
ResultType = NullToUndefined<SourceType>
>(object: SourceType) {
const source = object as {[key: string]: any};
const result: {[key: string]: any} = {};
for (const key in object) {
if (Object.hasOwn(object, key)) {
result[key] = source[key] ?? undefined;
}
}
return result as ResultType;
}
请注意 Object.hasOwn
非常 新,但很容易填充。或者您可以改用 Object.prototype.hasOwn.call(object, key)
。
(在这两种情况下 within nullToUndefined
我在类型断言方面玩得有点快和松散。对于像这样的小实用函数,我认为这是一个合理的妥协,前提是输入和输出定义明确。)
然后:
export default function Example({ ExampleProps }) {
const {
content = "",
title = "Missing Title",
date = "",
featuredImage = {},
author = {},
tags = [],
} = nullToUndefined(ExampleProps || {});
// ^^^^^^^^^^^^^^^^−−−−−−−−−−−−−−−−−−^
// ...