是否可以使用通用类型来确定运行时功能? [英] Is it possible to use the generic type to determine runtime functionality?

查看:38
本文介绍了是否可以使用通用类型来确定运行时功能?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遇到的情况可能是这样的:

I have a situation where I might have some code like this:

const data = fetchSomeData(); //Can be anything 
const checkedData = checkDataIs<SomeSpecificType>(data); 

// now I can treat checkedData as SomeSpecificType

也就是说,在编译时,我知道数据应该是什么形状,并且想要这样写代码,具有类型检查的所有优点,但是在运行时,我需要先运行一个函数来检查它.(如果数据不匹配,则会抛出错误.)

That is, at compile time I know what shape the data should be, and want to write my code as such, have all the advantages of type checking, but at runtime I need to run a function first to check that. (And it'll throw an error if the data doesn't match).

我知道我可以通过为要检查的每种类型编写单独的函数来实现此功能,例如:

I know that I can achieve this functionality by writing individual functions for each type I want to check, eg:

type Foo = {
    id: number; 
}

type Bar = {
    name: string; 
}

function checkFoo(data: any) : Foo {
      try {
          if (typeof data.id === 'number'){
              return data as Foo; 
          }
          else {
              throw new Error(); 
          }
      }catch (e) {
          throw new ("Failed validation")
      }
}

function checkBar(data : any) : Bar {
      try {
          if (typeof data.name === 'string'){
              return data as Bar; 
          }
          else {
              throw new Error(); 
          }
      }catch (e) {
          throw new ("Failed validation")
      }
}

这可以正常工作.

问题是-我可以创建一个采用通用参数T的函数,以确定在运行时运行哪些功能吗?

The question is - could I create a function that takes a generic param T, to determine which functionality is run at runtime?

例如:

function checkItem<T extends Foo|Bar>(data:any) : T {

    // if T is Foo
    return checkFoo(data); 

    //If T is Bar
    return checkBar(data); 

}

从技术上讲,在我们不要求了解运行时类型的意义上,这应该是可能的-这将根据通用参数是什么而编译不同的代码.

I think technically this should be possible in the sense that we're not asking for runtime type awareness - what this would be doing is compiling different code depending on what the generic parameter is.

推荐答案

此问题的规范答案是直截了当的您不能这样做".这是 TypeScript的非目标 >

The canonical answer to this question is a blunt "you can't do this". It's a non-goal of TypeScript to

添加或依赖程序中的运行时类型信息,或根据类型系统的结果发出不同的代码.相反,应鼓励不需要运行时元数据的编程模式.

add or rely on run-time type information in programs, or emit different code based on the results of the type system. Instead, encourage programming patterns that do not require run-time metadata.


答案可以止于此,但是了解如何获得与此类似的 行为可能会有所帮助,而无需TypeScript为不同类型发出不同的代码.在这种情况下,我会写一些用户定义的类型保护函数,根据输入参数是否为保护类型返回布尔值.这样说:


The answer could just stop there, but it might be helpful to see how to get similar behavior to this without requiring TypeScript to emit different code for different types. In this case, I'd write some user-defined type guard functions which return boolean values based on whether or not the input argument is of the guarded type. Say like this:

namespace Type {
    export function Foo(x: any): x is Foo {
        return x && "id" in x && typeof x.id === "number";
    }
    export function Bar(x: any): x is Bar {
        return x && "name" in x && typeof x.name === "string";
    }
}

此处, Type.Foo Type.Bar checkFoo() checkBar()中的逻辑相似函数.并请注意,尽管我们已将它们添加到类型系统中,并具有可用于以下目的的属性,但 Type.Foo Type.Bar 是运行时确实存在的函数.将 any 的值缩小为 Foo Bar .

Here, Type.Foo and Type.Bar are similar to the logic in your checkFoo() and checkBar() functions. And note that Type.Foo and Type.Bar are functions that do exist at runtime, although we have imbued them in the type system with the property that they can be used to narrow an any value to a Foo or a Bar.

然后我们可以这样编写您的 checkItem()函数:

Then we can write your checkItem() function like this:

function checkItem<T>(guard: (x: any) => x is T, data: any): T {
    if (!guard(data)) {
        throw "Failed validation";
    } else {
        return data;
    }
}

您可以认为 checkItem 不是采用运行时将消失的 type参数(尽管它是通用的),而是采用了 type防护函数将存在.

You can think of checkItem as not taking a type parameter (although it is generic) which will be gone at runtime, but as taking a type guard function which will exist.

如果您有可能是或不是 Foo

If you have something which might or might not be a Foo

const maybeFoo = JSON.parse(Math.random() < 0.5 ? '{"id": 123}' : '{"di": 321}');

然后您可以像这样调用 checkItem():

Then you can call checkItem() like this:

const foo: Foo = checkItem(Type.Foo, maybeFoo); // could be Failed validation here
console.log(foo.id.toFixed(1)); // 123.0 if it reaches here

我们已经将 checkItem< Foo>(maybeFoo)更改为 checkItem(Type.Foo,也许Foo)同一件事.我们都以两种方式指定 Foo ,但是后一种方式是通过运行时存在的东西来指定.

We've changed from checkItem<Foo>(maybeFoo) to checkItem(Type.Foo, maybeFoo), which, if you squint at it enough, is sort of the same thing. We are specifying Foo both ways, but the latter way is doing so with a thing that exists at runtime.

游乐场链接代码

这篇关于是否可以使用通用类型来确定运行时功能?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆