简体   繁体   English

通用 function 获取具有默认值的可选属性值

[英]Generic function getting optional property value with default value

There is an object type like below X .有一个 object 类型,如下面的X Note that all properties are optional, their types are different, and for some properties, null is a valid value.请注意,所有属性都是可选的,它们的类型不同,并且对于某些属性, null是一个有效值。

type X = {
  a?: number | null;
  b?: string;
  c?: boolean | null;
};

Now, for convenience, I want to make a function to get one of the X 's property values.现在,为了方便起见,我想制作一个 function 来获取X的属性值之一。 The function can accept a default value and return it if the property is missing. function 可以接受默认值并在属性缺失时返回它。

The function I wrote is below:我写的function如下:

function get<K extends keyof X>(k: K, x: X, defaultValue: Required<X>[K]): Required<X>[K] {
  const v = x[k];
  return v !== undefined ? v : defaultValue;
}

Because the properties are optional, I used Required<X>[K] to get the valid property type (excluding undefined ).因为属性是可选的,所以我使用Required<X>[K]来获取有效的属性类型(不包括undefined )。

My expected results are:我的预期结果是:

// a: number | null = 12345
const a = get('a', {}, 12345);
// b: string = 'bar'
const b = get('b', { b: 'bar' }, '?');
// c: boolean | null = null
const c = get('c', {}, null);

But I got the following compiler error at the return statement of the get function.但是我在get function 的返回语句中得到了以下编译器错误。

Type 'Required<X>[K] | (X[K] & ({} | null))' is not assignable to type 'Required<X>[K]'.
  Type 'X[K] & null' is not assignable to type 'Required<X>[K]'.
    Type 'X[K] & null' is not assignable to type 'never'.

I guess there is a mistake in the function (probably around the use of Required ), but I can't solve it.我猜 function 中有错误(可能与Required的使用有关),但我无法解决。

Thank you.谢谢你。

When you are testing for v !== undefined TypeScript will narrow the type of v to X[K] & ({} | null) .当您测试v !== undefined TypeScript 时,会将v的类型缩小为X[K] & ({} | null) This intersection type, excludes undefined from the value type using an intersection.此交集类型使用交集从值类型中排除未定义。

Required<X>[K] does a similar thing, but there is actually a corner case where it does not remove undefined from K , when the property type includes undefined , but the property is not optional: Required<X>[K]做类似的事情,但实际上有一个极端情况,当属性类型包含undefined时,它不会从K中删除undefined ,但该属性不是可选的:

//number | undefined
type XX = Required<{ o: undefined | number }>['o']

Playground Link 游乐场链接

Even though Required<X>[K] is more permissive than X[K] & ({} | null) (allowing X[K] - optional undefined ) TypeScript will not be able to follow the mapped type to arrive at this conclusion.即使Required<X>[K]X[K] & ({} | null)更宽松(允许X[K] - optional undefined )TypeScript 将无法按照映射类型得出此结论。

The simplest solution would be to define a NotUndefined undefined that is similar to how TypeScript excludes undefined from v that we can use for both the return value and for the defautlValue parameter:最简单的解决方案是定义一个NotUndefined undefined,类似于 TypeScript 从v中排除undefined的方式,我们可以将其用于返回值和defautlValue参数:

type NotUndefined<T> = T & ({} | null)

function get<K extends keyof X>(k: K, x: X, defaultValue: NotUndefined<X[K]>): NotUndefined<X[K]>  {
  const v = x[k];
  return v !== undefined ? v : defaultValue;
}

Playground Link 游乐场链接

The version above does not allow undefined for the default value if the property was required and it had undefined explicitly in it's type (ex { d: boolean | null | undefined; } ).如果该属性是必需的,并且它的类型中显式undefined ,则上面的版本不允许默认值undefined (例如{ d: boolean | null | undefined; } )。

You could preserve the original behavior, by reverting defaultValue back to Required<X>[K] and adding it in a union with NotUndefined<X[K]> to the return type:您可以保留原始行为,方法是将defaultValue恢复为Required<X>[K]并将其添加到与NotUndefined<X[K]>的联合中以返回类型:

function get<K extends keyof X>(k: K, x: X, defaultValue: Required<X>[K]): Required<X>[K] | NotUndefined<X[K]>  {
  const v = x[k];
  return v !== undefined ? v : defaultValue;
}

Playground Link 游乐场链接

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM