当前位置: 首页 > 知识库问答 >
问题:

类型脚本通用约束

任小云
2023-03-14

我试图编写一个通用的getter函数,给定一个键,该键应该只返回类型字符串| boolean | number。因此,默认值应与返回值的类型相同

这是我尝试过的。

getSomething = async<T extends string | boolean | number>
    (key: string, defaultValue: T): Promise<T> => {
      const foo = bar.getValue("foo"); // return type is undefined|string|boolean|number
      return foo === undefined ? defaultValue : foo;
 }

我得到这个错误,不确定是什么错误。

Type 'string | number | boolean | T' is not assignable to type 'T'.
  'string | number | boolean | T' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'string | number | boolean'.
    Type 'string' is not assignable to type 'T'.
      'string' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'string | number | boolean'.ts(2322)

什么可能是“字符串|数字|布尔”的子类型让我感到困惑。它们不是已经是基本类型了吗?

共有2个答案

夏侯阳
2023-03-14

我不得不将foo转换为T

改变

return foo === undefined ? defaultValue : foo;

return foo === undefined ? defaultValue : foo as T;

我不确定为什么我需要这个显式转换,因为foo肯定是T,因为它不是未定义的。而T被限制为类型string | boolean | number

夏季萌
2023-03-14

如果没有bar的定义,这不是一个最小的可复制示例。因为我所知道的是bar.getValue()返回类型为字符串|数字|布尔值|未定义的的值,我将这样制作我自己的bar

const bar = { getValue: (x: string): string | number | boolean | undefined => 12345 };

然后我编写这段代码,它没有编译器错误:

const str = Math.random().toString();
getSomething("baz", str).then(v => console.log(v.toUpperCase()));

如果在TypeScript IDE中使用IntelliSense检查v的类型,它将是string。这是因为getSomething()的调用签名表示它将返回与第二个参数相同类型的promise。我们传入了str,一个string,因此我们得到了一个promise

哎呀,没有。运行代码,你会得到一个运行时错误和一条消息,如TypeError:v.toUpperCase不是一个函数。因为在运行时,v将是12345,即从bar.getValue()返回的实际值。而12345是一个数字,它没有toUpperCase方法。不知何故,我们遇到了一个数字被误认为字符串的情况。错误在哪里?

这正是编译器警告您的地方:

return foo === undefined ? defaultValue : foo; // error!
// Type 'string | number | boolean | T' is not assignable to type 'T'.

TypeScript告诉您,您应该返回类型为T的值,但是编译器只能验证您返回的类型为字符串|数字|布尔|T的值。在上面的例子中,Tstring,所以您可以将错误解释为类似于“您声称返回一个string,但我只知道您返回了一个字符串|数字|布尔值,这可能是一个字符串,但可能是一个数字或一个布尔值,在这种情况下,您的声明是不正确的,坏事可能会发生"。

希望你能理解为什么这是个问题T可以是stringnumberboolean,它们都比联合类型string | number | boolean窄。您可以将T分配给string | number | boolean,但反之亦然。

关于这个问题:“什么可能是字符串|数字|布尔的子类型让我困惑。他们不是已经是原始类型了吗?"那么,stringstring|number|boolean的子类型。联合A|B是其每个成员AB的超类型。

此外,即使您只有字符串数字布尔,TypeScript中也有这些类型的子类型:有字符串文字类型,如类型“foo”,数字文字类型,如类型123,甚至布尔文字类型truefalse(在这里和其他地方提到过)。这些文字类型表示特定的值。因此类型“a”“b”string的子类型,类型12number的子类型,类型truefalseboolean的子类型。

因此,实际上,当您使用第二个参数作为字符串、数字或布尔文本调用getSomething()时,这就是T的推断:

const p = getSomething("qux", "peanut butter and jelly");
// const p: Promise<"peanut butter and jelly">

因此,p不仅表示字符串的promise(这在一般情况下是不正确的),它实际上还表示特定字符串的promise“花生酱和果冻”。哎呀。

那么我们如何修复这段代码呢?嗯,这在很大程度上取决于你的用例。乍一看,我认为它根本不应该是通用的,只允许输入和输出都是字符串|数字|布尔值:

const getSomething2 = async (key: string, defaultValue: string | number | boolean):
    Promise<string | number | boolean> => {
    const foo = bar.getValue("foo"); 
    return foo === undefined ? defaultValue : foo;
}

没有错误的编译,然后有运行时错误但没有编译器错误的早期代码现在给你一个很好的编译器错误:

getSomething2("baz", str).then(v => console.log(v.toUpperCase())); // error!
// ---------------------------------------------> ~~~~~~~~~~~
// Property 'toUpperCase' does not exist on type 'number'.

v现在已知为string|number|boolean,并且您不能在此基础上调用toUpperCase方法,因为它可能是numberboolean

您可能需要getSomething()作为泛型,但在这种情况下,什么bar才是真正重要的。getValue()不需要,并且可能需要条。getValue()的签名可以修改,或者在getSomething()中的某个地方使用明智的类型断言,在该断言中,您负责验证编译器无法验证的内容,并在运行时处理断言不真实的后果。您的答案是return foo===未定义?默认值:foo作为T不太可能是正确的断言类型,特别是在文字类型方面。不过,我不会进一步推测这种方法。只需说一下,当您使用类型断言来消除编译器错误时,您需要仔细考虑您所做的声明。

好的,希望能有帮助!祝你好运

操场连结至守则

 类似资料:
  • 考虑以下TS定义: 没什么问题,但我想知道是否有一个等效?显然,可以将泛型传递到接口中,尽管这不是我想要的,例如: 这里的示例是在React代码的上下文中,但潜在的问题是基本的TS。

  • 问题内容: 我试图弄清楚如何为通用类(在Swift中)实现类型约束,它将通用类型限制为仅数字类型。例如Double,Int等,但不是字符串。谢谢你的帮助。 问题答案: 您可以使用尖括号为通用类(对函数使用相同的语法)指定类型约束(使用类和协议): 要在单个类型上指定多个需求,请使用子句: 但是,您似乎无法在泛型参数子句中指定可选要求,因此一种可行的解决方案是创建一个协议,使所有数字类型都通过扩展来

  • 我在TypeScript 2.4中遇到一个错误。2在泛型类上,该泛型类的约束与不太严格的接口不兼容。我得到以下错误: TS/组件/Schedule.ts(37,13):错误TS2322:键入{人周末视图: PlanItemschduleView; project...属性“人员周末视图”与索引签名不兼容。类型PlanItemschduleView不能分配给类型IPlanItemschduleVie

  • 我试图在我的主文件。 entry.ts位于src/js/entry, ts,vue的类型定义位于src/js/lib/baul/vue/type/index.d.ts 编译此文件时,输出如下。顺便说一句,typescript的目标是es6和es6模块。 JQuery工作正常(看起来像),所以我用导入替换了对vue类型定义的引用。 它编译得很好,下面是输出: 问题是没有删除类型定义的import语句

  • 销关节 cpPinJoint *cpPinJointAlloc(void) cpPinJoint *cpPinJointInit(cpPinJoint *joint, cpBody *a, cpBody *b, cpVect anchr1, cpVect anchr2) cpConstraint *cpPinJointNew(cpBody *a, cpBody *b, cpVect anchr1,

  • 泛型的类型约束 swapTwoValues(_:_:)函数和Stack类型可以用于任意类型. 但是, 有时在用于泛型函数的类型和泛型类型上, 强制其遵循特定的类型约束很有用. 类型约束指出一个类型形式参数必须继承自特定类, 或者遵循一个特定的协议、组合协议. 例如, Swift的Dictionary类型在可以用于字典中键的类型上设置了一个限制. 如字典中描述的一样,字典键的类型必须是可哈希的. 也