简体   繁体   中英

Typescript use parameter type inference with defaults

I have this function as below that works well:

function pickAttributes<K extends string, T> (e: Element, attrs: K[]): Record<K, string> {
  return attrs.reduce((obj, key) => {
    return {
      ...(obj as any),
      [key]: e.getAttribute(key)
    }
  }, {})
}

I want to be able to have this function take an optional map function to convert the attributes to a different type.

function pickAttributes<K extends string, T> (e: Element, attrs: K[], mapFn?: (attr: null | string) => T): Record<K, T> {
  if (!mapFn) mapFn = x => String(x)
  return attrs.reduce((obj, key) => {
    return {
      ...(obj as any),
      [key]: mapFn(e.getAttribute(key))
    }
  }, {})
}

However i get this error

TS2322: 
Type '(x: string | null) => string' is not assignable to type '((attr: string | null) => T) | undefined'. 
  Type '(x: string | null) => string' is not assignable to type '(attr: string | null) => T'.
     Type 'string' is not assignable to type 'T'.

I get a similar error if i try to use a default parameter.

The changed function seems to work as expected without trying to add a default

function pickAttributes<K extends string, T> (e: Element, attrs: K[], mapFn?: (attr: null | string) => T): Record<K, T> {
  return attrs.reduce((obj, key) => {
    return {
      ...(obj as any),
      [key]: e.getAttribute(key)
    }
  }, {})
}

First: I'm not sure what could extend string, so I would drop the generic parameter K.

But anyway: you want your return type to vary, based on the presence of the mapFn argument. I would solve that by defining two overloads of the function. And in the implementation, by using a union type string | T string | T :

function pickAttributes<K extends string>(e: Element, attrs: K[]): Record<K, string>;
function pickAttributes<K extends string, T> (e: Element, attrs: K[], mapFn: (attr: null | string) => T): Record<K, T>;
function pickAttributes<K extends string, T> (e: Element, attrs: K[], mapFn?: (attr: null | string) => T): Record<K, string | T> {
  const fn = mapFn || (x => String(x));
  return attrs.reduce((obj, key) => {
    return {
      ...(obj as any),
      [key]: fn(e.getAttribute(key))
    }
  }, {});
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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