【发布时间】:2019-09-25 16:14:08
【问题描述】:
我一直在尝试为下面的函数useDefaults 提出泛型类型:
type ValuesOf<T extends readonly any[] | undefined> = T extends readonly any[] ? T[number] : never;
type RequiredAndDefined<T, K extends keyof T> = {
[P in K]-?: Exclude<T[P], undefined>;
};
export type EnforcedDefaultProps<P, K extends keyof P> = K extends never ? P : Omit<P, K> & RequiredAndDefined<P, K>;
export type ArrayEnforcedDefaultProps<P, K extends ReadonlyArray<keyof P> | undefined> = EnforcedDefaultProps<P, ValuesOf<K>>;
export const useDefaults = <P extends object, KEYS extends keyof P>(
props: P | undefined,
defaultProps: ArrayEnforcedDefaultProps<P, typeof enforcedDefaults>,
enforcedDefaults?: ReadonlyArray<KEYS>,
): ArrayEnforcedDefaultProps<P, typeof enforcedDefaults> => {
const newProps: P = { ...defaultProps, ...props };
if (enforcedDefaults) { // if user explicitly passes undefined to the prop, default prop will not be used unless the key is in enforcedDefaults
enforcedDefaults
.filter((key) => newProps[key] === undefined)
.forEach((key) => newProps[key] = defaultProps[key]);
}
return newProps as ArrayEnforcedDefaultProps<P, typeof enforcedDefaults>;
};
此函数将对两个对象执行基于分配的简单操作:一个包含用户提交的选项,另一个包含函数开发人员设置的默认值。然后,它将选择性地为某些属性(函数开发人员提供)强制执行未定义的值,以便useDefaults 的返回对象的类型消除了这些某些属性中的每一个的值都是undefined 的可能性。这避免了函数开发者必须使用非空断言,同时也不会给函数用户带来负担。
所需用法示例:
interface FormatOpts {
maxLength?: number,
prefix?: string,
suffix?: string,
}
const format = (value: string, _opts?: FormatOpts) => {
const opts = useDefaults(_opts, {
prefix: "",
suffix: "",
}, ["prefix", "suffix"]);
// prefix and suffix are guaranteed to not be `undefined` at this point, but are not explicitly required to be specified in the `_opts` object by the user of the function
const modifiedPrefix = prefix.toUpperCase(); // want to avoid things like prefix!.toUpperCase();
}
// examples with "prefix" prop as a focus:
format("ASDF"); // OK -> prefix after useDefaults: ""
format("ASDF", { prefix: "MyPrefix" }); // OK -> prefix after useDefaults: "MyPrefix"
format("ASDF", { prefix: undefined }); // OK -> prefix after useDefaults: ""
format("ASDF", { suffix: "test" }); // OK -> prefix after useDefaults: ""
我希望使其尽可能动态(推断出尽可能多的类型)。
虽然这段代码确实可以编译,但输入却以某种方式关闭。它不是在需要的地方删除undefineds 的单一对象类型,而是看起来是每个可能组合的联合。有没有更简单的方法来做我想做的事情或将这些组合变平?
屏幕截图具有生成的确切类型:
#2 我想要的类型是
{
readonly maxLength?: string | undefined;
readonly prefix: string;
readonly suffix: string;
}
【问题讨论】:
-
这并不完全构成minimal reproducible example,因为我想您的问题类似于“
EnforcedDefaultProps<FormatOpts, "prefix" | "suffix">不是我期望的类型”,但这并未在任何地方列出在您的代码中,还有一堆额外的代码掩盖了关键问题。您是否可能只需要type EnforcedDefaultProps<P, K extends keyof P> = [K] extends [never] ? P : Omit<P, K> & RequiredAndDefined<P, K>;?也就是说,不要distribute the conditional type超过K? -
谢谢。经过更多的实验,我现在期望至少在我的一种用法上打字。发布作为参考答案。
标签: typescript generics type-inference