简体   繁体   English

如何在 TypeScript 中缩小这种类型?

[英]How to narrow this type in TypeScript?

The following sample code does not pass the type check.以下示例代码未通过类型检查。 I would like to find a way to make it pass without as casting, if possible.如果可能的话,我想找到一种方法让它通过而as铸造。

type SupportedHandlerType = string | number | Date
type Handler<T> = (data: T[]) => void

function example<T extends SupportedHandlerType>(data: T[]) {
  const handler = getHandler(data)
  handler(data)
}

function stringHandler(data: string[]) {

}

function numberHandler(data: number[]) {
    
}

function dateHandler(data: Date[]) {
    
}

function getHandler<T>(data: T[]): Handler<T> {
    const first = data[0]
    if (typeof first == 'string') {
        return stringHandler // Type 'T' is not assignable to type 'string'
    }
    if (typeof first == 'number') {
        return numberHandler // another error here
    }
    return dateHandler // and here
}

My real life version is much more complicated, but this simple piece show cases the problem I have: I have a few types forming a union ( SupportedHandlerType ), and the generic function is called with one of those.我的现实生活版本要复杂得多,但这个简单的部分显示了我遇到的问题:我有几个类型形成一个联合( SupportedHandlerType ),通用 function 被其中一个调用。 I have a getHandler() , which dynamically looks at the types to determine which one it is and returns the appropriate handler function, which has a dedicated type instead of T .我有一个getHandler() ,它动态查看类型以确定它是哪个类型并返回适当的处理程序 function,它有一个专用类型而不是T

Is there a way to somehow narrow the types so that I don't get errors such as Type 'T' is not assignable to type 'string' ?有没有办法以某种方式缩小类型,以免出现诸如Type 'T' is not assignable to type 'string'错误?

Playground操场

Arrays lose their typing once they are transpiled to javascript. Arrays 一旦被转译为 javascript,就会丢失它们的类型。 Meaning, that you can't be sure what the type of an array is, except if you iterate through them and check the value of each element.这意味着,您无法确定数组的类型是什么,除非您遍历它们并检查每个元素的值。

That's why I would add these 2 functions.这就是为什么我要添加这两个功能。

function isArrayOfStrings(value: unknown): value is string[] {
  return Array.isArray(value) && value.every(item => typeof item === "string");
}

function isArrayOfNumbers(value: unknown): value is number[] {
  return Array.isArray(value) && value.every(item => typeof item === "number");
}

More importantly, notice the return type of these functions.更重要的是,请注意这些函数的返回类型。 These kind of functions are sometimes called "type-guards".这些函数有时被称为“类型保护”。 the ... is... return-value-type indicates that when the function returns true, this also means that we can conclude that the input variable was of a certain type. ... is... return-value-type 表示当 function 返回 true 时,这也意味着我们可以断定输入变量是某种类型。

Finally, I changed something to the T generic.最后,我将某些内容更改为T泛型。 You can make them extend something.你可以让它们extend一些东西。 By doing so, I simplified the generics and indicated that the input T should always be an array.通过这样做,我简化了 generics 并指出输入T应该始终是一个数组。

type Handler<T extends any[]> = (data: T) => void

function getHandler<T extends any[]>(data: T): Handler<T> {
  if (isArrayOfStrings(data)) {
    return stringHandler;
  }
  if (isArrayOfNumbers(data)) {
    return numberHandler;
  }
  return dateHandler;
}

Alternatively, if you don't want to loop through the entire arrays, you could simplify the isArrayOf... functions by just checking the type of the first element.或者,如果您不想遍历整个 arrays,您可以通过检查第一个元素的类型来简化isArrayOf...函数。

The issue is related to how you are declaring the handler functions.该问题与您如何声明处理程序函数有关。 You have to stick to the same structure declared in type Handler您必须坚持在type Handler中声明的相同结构

type SupportedHandlerType = string | number | Date
type Handler<T> = (data: T[]) => void

function example<T extends SupportedHandlerType>(data: T[]) {
  const handler = getHandler(data)
  handler(data)
}

function stringHandler<T = string>(data: T[]) {

}

function numberHandler<T = number>(data: T[]) {
    
}

function dateHandler<T = Date>(data: T[]) {
    
}

function getHandler<T>(data: T[]): Handler<T> {
    const first = data[0]
    if (typeof data == 'string') {
        return stringHandler // Type 'T' is not assignable to type 'string'
    }
    if (typeof data == 'number') {
        return numberHandler // another error here
    }
    return dateHandler // and here
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM