首页 > 解决方案 > 如何根据输入参数中的键推断数据?

问题描述

不太清楚为什么这不起作用。

type someTypes = "a" | "b" | "c"
type otherTypes = "d" | "e" | "f"

interface someObject   {
  foo: string
}

type Data<K> = K extends someTypes ? string : someObject

const doSomething = <K extends someTypes|otherTypes>(key:K, data: Data<K>) => {
  switch (key) {
    case "b":
      console.log(data.length);
      break;
    case "e":
      console.log(data.foo);
      break;
  }
}

我想要实现的是,对于某些键值,数据具有一种形状,而另一些则具有另一种形状。如果这会超载签名将是这些中的任何一个:

const doSomething = (key: someTypes, data: string) => { }
const doSomething = (key: otherTypes, data: someObject) => { }

ts-游乐场

标签: typescript

解决方案


这里的问题是类型参数K 不能控制流switch)缩小。一个例子:

function fooGeneric<T extends "a" | "b">(t: T) {
  if (t === "a") {
    t // t is still T extends "a" | "b" 
  }
}

function fooConcrete(t: "a" | "b") {
  if (t === "a") {
    t // t is now "a" as expected
  }
}

条件类型Data<K>也不会缩小,所以 TS 不知道函数参数在语句data分支中的样子。switch

解决方案:在这里只使用类型断言是好的和安全的,因为您已经检查了key.

const doSomething = <K extends someTypes|otherTypes>(key:K, data: Data<K>) => {
  switch (key) {
    case "b":
      console.log((data as Data<"b">).length);
      break;
    case "e":
      console.log((data as Data<"e">).foo);
      break;
  }
}

或者,您可以doSomething具体化并使用映射Data类型(这个会改变函数签名):

type DataAlt = { [K in someTypes]: string } & { [K in otherTypes]: someObject }

const doSomethingAlt = (key: keyof DataAlt, data: DataAlt) => {
  switch (key) {
    case "b":
      const resB = data[key] // const resB: string
      resB.length // ✅
      break;
    case "e":
      const resE = data[key] // const resE: someObject
      resE.foo // ✅
      break;
  }
}

操场


推荐阅读