打字稿中的从属类型-通过属性名称类型确定类型

时间:2020-11-01 12:28:51

标签: typescript dependent-type

我试图使类型取决于所传递参数的类型。 一个人为的例子是:

type NS = "num" | "str"
type Data<T extends NS> = T extends "num" ? number : string
type Func<T extends NS> = (x: Data<T>) => Data<T>
type Funcs = {[T in NS]: Func<T>}
type Obj = {[T in NS]: Data<T>}
const funcs: Funcs = {
  num: (x) => x * 2,
  str: (x) => x + x
}

function useFunc<T extends NS>(ns: T, obj: Obj): Data<T> {
  const f = funcs[ns]
  const x = obj[ns]
  return f(x)
}

问题是f(x)的类型为number | string而不是Data<T>,这似乎是由f的类型为Funcs[T]而不是Func<T>引起的。 x的类型也为Obj[T],而不是Data<T>

因此即使Funcs[T]Func<T>Obj[T]Data<T>Funcs是相同的,并且Objfunction useFunc<T extends NS>(ns: T, obj: Obj): Data<T> { const f = funcs[ns] as Func<T> const x = obj[ns] as unknown as Data<T> // note the double-cast as Obj[T] and Data<T> "do not sufficiently overlap" return f(x) } 是相同的,打字稿也不能推断出它们相同。 function useFunc<T extends NS>(ns: T, obj: Obj): Data<T> function useFunc(ns: NS, obj: Obj): Data<NS> { if (ns === "num") { const f = funcs[ns] const x = obj[ns] return f(x) } const f = funcs[ns] const x = obj[ns] return f(x) } 已定义...

我可以通过类型转换使它正常工作,但是它违反了使用类型的目的:

IIF([Data From System$].[Data Dim#] IS NULL,
    [Data From System$].[Data Dim#]
   ) AS [Value]

有没有办法使它在打字稿中起作用?

编辑:

我知道可以通过这种方式使其工作,并且可以在调用站点上使用,但是从代码重用的角度来看毫无意义-在不同类型上组合相同的逻辑是这样做的实际动力,并且这样就可以简单地重复。

IIF()

有没有办法避免这种重复而又不会失去类型安全性?

1 个答案:

答案 0 :(得分:0)

主要问题是,当TS推断f签名为useFunc时,您试图在f内部调用(x: never) => string | numer函数。此签名与funcs对象中的每个签名都不兼容。

另外,正如我所说,应将签名useFunc<T extends NS>(ns: T, obj: Obj): Data<T>简化为useFunc<T extends NS>(ns: T, obj: Data<T>): Data<T>,甚至最好将其更正。这是因为TS应该知道您将传递给f的数据的类型,以便能够进行类型检查。另外,另一个需要进行上述修改的原因是useFunc函数调用仅在ns等于"num""str"的情况下可用,但不能同时使用。由于上述原因,obj参数类型将为Data<"num">Data<"str">。因此,将其键入为Obj是错误的。

可能的更改最少的解决方案是:

function useFunc<T extends NS>(ns: T, arg: Data<T>): Data<T> {
  const f = funcs[ns]
  return (f as Func<T>)(arg)
}