Typescript 可选泛型 属性

Typescript optional generic property

我有这种类型,其中我的 value 属性 是 "optional"(如果 T 不是 undefined

type AsyncState<T = undefined> = {
    value?: T;
    loading: boolean;
    error: { reason: string } | null;
}

现在我需要以某种方式创建依赖于 AsyncState 参数的新对象 - 添加 value 属性 如果 T 不是 undefined 并且不要t 如果 T 未定义。 (这只是更复杂逻辑的虚拟示例,但由于类型是问题,它应该足够了)

function asyncGet<T>(initialState: AsyncState<T>) {
    return typeof initialState.value !== 'undefined' 
        ? (s: AsyncState<T>) => ({ ...initialState })
        : (s: AsyncState) => ({ loading: initialState.loading, error: initialState.error });
}


const first: AsyncState<string> = {
    loading: true,
    error: null,
    value: ""
}

const second: AsyncState<string> = {
    loading: true,
    error: null,
    value: ""
}

const creator = asyncGet(first);

/* 
Argument of type 'AsyncState<string>' is not assignable to parameter of type 'AsyncState<string> & AsyncState<undefined>'.
  Type 'AsyncState<string>' is not assignable to type 'AsyncState<undefined>'.
    Type 'string' is not assignable to type 'undefined'.
*/
creator(second);

这里是typescript playground.

您可以通过使返回的函数需要推断 T 实际 类型泛型来解决此问题。

function asyncGet<T>(initialState: AsyncState<T>) {
  return typeof initialState.value !== "undefined"
    ? (s: AsyncState<T>) => ({ ...initialState })
    : <U>(s: AsyncState<U>) => ({
        loading: initialState.loading,
        error: initialState.error
      });
}

也就是说,如果您试图通过这样调用来覆盖 TypeScript 的推断,这会给您带来麻烦:asyncGet<string>({ loading: true, error: null })

更好的解决方案是使用条件类型指定函数有条件地使用调用返回函数的推断值。

function asyncGet<T>(initialState: AsyncState<T>): 
  <U>(s: AsyncState<[T] extends [undefined] ? U : T>) => 
    AsyncState<[T] extends [undefined] ? U : T> {

  return typeof initialState.value !== "undefined"
    ? (s) => ({ ...initialState })
    : (s) => ({
        loading: initialState.loading,
        error: initialState.error
      });
}