1

我在这个操场上有以下代码:

export enum HTTPMethod {
  GET = 'GET',
}

export type FetchData<TData> = (
  routeOrBody?: string | BodyInit | object,
  body?: BodyInit | object,
) => Promise<TData | undefined>

export type AbortFetch = () => void;

export interface FetchCommands<TData> {
  get: FetchData<TData>
  abort: AbortFetch
}

export interface UseFetchResult<TData>{
  data: TData,
  request: FetchCommands<TData>
}

export type FetchResult<TKey extends keyof FetchCommands<TData>, TData> = {
  data: TData
} & {
  [key in TKey]: any
}

const makeHttpVerbMethod = <TData, TMethod extends keyof FetchCommands<TData>>(
  httpMethod: HTTPMethod,
) => (incoming: TData): FetchResult<TMethod, TData> => {

  const methods: FetchCommands<TData> = {
    get: () => Promise.resolve(undefined),
    abort: () => undefined
  } 

  const {request}: UseFetchResult<TData> = { data: incoming, request: methods }

  const commandKey = httpMethod.toLowerCase() as TMethod

  const httpFunc = request[commandKey]


  const result: FetchResult<TMethod, TData> =  {
    data: incoming,
    [commandKey]: httpFunc,
  }

  return result
}

基本上,我想为makeHttpVerbMethod函数返回的内容添加一个键,但我不完全确定这是否可能,我正在使用泛型类型参数来尝试限制可以调用的键:

const makeHttpVerbMethod = <TData, TMethod extends keyof FetchCommands<TData>>(
  httpMethod: HTTPMethod,
) => (incoming: TData): FetchResult<TMethod, TData> => {

我想为返回类型添加一个动态键makeHttpVerbMethod

但我认为 tsc 感到困惑,因为我在尝试分配给result.

键入'{ [x:字符串]:TData | FetchCommands[TMethod]; 数据:TData;}' 不可分配给类型 'FetchResult'。键入'{ [x:字符串]:TData | FetchCommands[TMethod]; 数据:TData;}' 不可分配给类型 '{ [TMethod 中的键]: any; }'。

我不明白{ [x: string]从哪里来

如果我将函数更改为使用显式'get'文字,那么一切正常

const makeHttpVerbMethod = <TData, TMethod extends keyof FetchCommands<TData>>(
  httpMethod: HTTPMethod,
) => (incoming: TData): FetchResult<'get', TData> => {

  const methods: FetchCommands<TData> = {
    get: () => Promise.resolve(undefined),
    abort: () => undefined
  } 

  const {request}: UseFetchResult<TData> = { data: incoming, request: methods }

  const commandKey = 'get'

  const httpFunc = request[commandKey]

  const result: FetchResult<'get', TData> =  {
    data: incoming,
    [commandKey]: httpFunc,
  }

  return result
}

有没有办法使用参数化类型 TMethod 进行这项工作?

4

1 回答 1

0

这里的一个问题似乎是一个已知的错误,即具有计算属性的对象{[commandKey]: httpFunc}将其键扩展为字符串索引,这就是编译器抱怨string不匹配的原因TMethod。当您替换为仅"get"因为单个字符串文字类型的计算键未扩展为string. 现在还没有解决办法。只有解决方法。

我在这里建议的解决方法是使用一个辅助函数,它接受一个类型的键K和一个类型的值V并返回一个类型的对象Record<K, V>(意味着键是K,值是V):

function keyValue<K extends keyof any, V>(key: K, value: V) {
  const ret = {} as Record<K, V>;
  ret[key] = value;
  return ret;
}

如果K已知它只是单个键类型而不是联合,则这仅是类型安全的:

const goodUseOfKeyValue = keyValue("hey", "you"); // {hey: "you"}

const notGoodUseOfKeyValue = keyValue(Math.random() < 0.5 ? "oh" : "no", 1);
// supposedly type {oh: number, no: number},
// but of course it's really {oh: number} | {no: number}

(这可以修复,以便notGoodUseOfKeyValue正确键入,但我们在这里不需要它,因为您打算TMethod成为单个字符串文字)。

现在我们将使用它而Object.assign()不是使用计算属性:

  const result: FetchResult<TMethod, TData> = Object.assign(
    { data: incoming },
    keyValue(httpMethod, request[httpMethod])
  ); // okay, no error

好的,希望有帮助;祝你好运!

链接到代码

于 2019-07-20T19:56:37.870 回答