无法在打字稿中键入动态键

时间:2019-07-19 18:16:14

标签: typescript

我的代码格式为this playground

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:string]:TData | FetchCommands [TMethod];数据:TData; }'不可分配为类型'{[TMethod中的键]:任何; }'。

我不知道{ [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来实现此目的?

1 个答案:

答案 0 :(得分:0)

这里的问题之一似乎是known bug,据此,具有{[commandKey]: httpFunc}之类的已计算属性的对象将其键扩展为string index,这就是为什么编译器抱怨{{1 }}与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是单个字符串文字)。

现在,我们将使用TMethodObject.assign()来代替

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

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

Link to code