Typescript:如何用函数和参数满足联合类型

问题描述 投票:0回答:1

(arg: string) => void
(arg: number) => void
的并集是
(arg: never) => void
,因为没有值可以同时是
string
number
。但是在下面的示例中我该如何解决这个问题呢?

type Test<T, U> =
  | {
      func: (arg: T) => void;
      data: T;
    }
  | {
      func: (arg: U) => void;
      data: U;
    };

function test<T, U>(x: Test<T, U>) {
  x.func(x.data);
}

在此示例中,

x.data
应该始终是正确的类型以满足函数参数,但打字稿看不到它并且会出现
Argument of type 'string | number' is not assignable to parameter of type 'never'
错误。

编辑:我的用例如下:我们的应用程序有一些代码来处理页面之间的导航。每个可能的页面都由如下所示的内容表示:

type Route<T> = {
  extractData: (url: string) => T;
  encodeDataToURL: (data: T) => string;
  getTitle: (data: T) => string;
  render: (data: T) => DOM.Element;
}

路由器处理所有路由,因此调用当前路由的

extractData
会产生
HomePageData | SearchPageData | ...
。当我们调用
getTitle
render
时,因为它是生成数据的同一个对象,所以它应该仍然可以正常工作,但 typescript 似乎没有注意到这一点。

javascript typescript types union-types strong-typing
1个回答
0
投票

Typescript 就不是这样工作的。它只跟踪类型,而不跟踪变量之间的关系。如果您从对象中获取属性,Typescript 将不会记住您从哪里获取它。这只是类型的另一回事。

您的

Test
设计过于模糊,无法按原样使用。尝试将对象文字传递给
test
:

test({
  data: "hello",
  func: (s) => s // Parameter 's' implicitly has an 'any' type.
})

Typescript 不知道发生了什么,因为

Test
联合成员之间的唯一区别是抽象类型参数。它没有基础来选择您想要的联合成员,因此它无法将
func
的参数缩小到
string
和您未指定的其他抽象类型之间。

为了解决您的示例案例,

Test
中的联合和单独的泛型参数是完全多余的,当您摆脱它们时,问题就会消失。您提供的
Route
类型完全符合您的预期。

// no problems
function test<T>(x : Route<T>) {
  x.render(x.extractData("imma string"));
}

test({
  extractData: (url) => ({ stuff: "yes", junk: "no" }),
  encodeDataToURL: (data) => data.stuff,
  getTitle: (data) => data.junk,
  render: (data) => new Element()
})

在实践中,如果您正在使用使用不同数据类型的

Route
的并集,并且您希望函数统一作用于所有
Route
,则理想情况下,函数签名将表示数据类型并不重要.

function render(route : Route<any>) : Element {
  return route.render(route.extractData("gimme"));
}

type TargetRoute = Route<{ junk : number }> | Route<{ stuff : string }>

function renderTarget(target : TargetRoute) {
  render(target);
  // otherwise, you could do this:
  test<any>(target);
  // but if the function isn't acting on the generic data type anyway, 
  // you're obfuscating its purpose
}

最后,这个最小的情况不需要它们,但是如果您需要区分 Route 而无需先提取它们的数据,您可能会想要使用

discriminated unions

© www.soinside.com 2019 - 2024. All rights reserved.