- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
Typescript 没有内置的 Opaque
类型 like Flow does .所以我做了自己的定制Opaque
类型:
type Opaque<Type, Token = unknown> = Type & {readonly __TYPE__: Token}
never
(在区分工会时有用):
export function assertNever(value: never): never {
throw new Error(`Unhandled discriminated union member: ${JSON.stringify(value)}`);
}
__TYPE__
属性(property),我不能真正“区分”:
export function assertNever(value: never): never {
throw new Error(`Unhandled discriminated union member: ${JSON.stringify(value)}`);
}
type Opaque<Type, Token = unknown> = Type & { readonly __TYPE__: Token };
const animals = ["Dog" as const, "Cat" as const];
type Animal = Opaque<typeof animals[0], "Animal">;
function makeSound(animal: Animal) {
switch (animal) {
case "Dog":
return "Haw!";
case "Cat":
return "Meow";
default:
assertNever(animal);
// ^^^ discriminated unions won't work.
}
}
makeSound("Dog" as Animal);
最佳答案
我还没有弄清楚为什么你的Opaque<T, U>
使用 if
时,类型无法通过控制流分析正确缩小范围/else
或 switch
/case
陈述。当您传入 T
的原始数据类型时在 Opaque<T, U>
,如 Animal
与 "Cat" | "Dog"
,您会得到所谓的“品牌原语”,如 in this FAQ entry about making nominal types 所述.似乎正在发生的事情是当你有一个品牌原语 val
并对另一个原始值 somePrimitive
使用常规类型保护检查,如 if (val === somePrimitive) { /*true*/ } else { /*false*/ }
或 switch (val) { case somePrimitive: /* true */ break; /*false*/
},检查的“真实”部分一切正常:val
的类型缩小到 Extract<
typeof val, typeof somePrimitive>
.所以在你的情况下,if (animal === "Dog") { /*true*/ } else { /*false*/ }
缩小到 Opaque<"Dog", "Animal">
在真正的分支。
不好的是检查的“错误”部分发生了什么。如果 val
不等于 somePrimitive
,那么我们应该能够将其缩小到 Exclude<
typeof val, typeof somePrimitive>
.也就是说,当 animal
不等于 "Dog"
,编译器应该缩小 animal
至Opaque<"Cat", "Animal">
.但这并没有发生。
有时在这样的检查中,不缩小错误分支是正确的。例如,当您的类型不是单例并且可以具有多个该类型的有效值时。如果我有 function f(x: string | number) { if (x === "Dog") { /*true*/ } else { /*false* } }
, 缩小 x
是有意义的至string
(甚至 "Dog"
)在真正的分支中,但你不想缩小 x
至number
在假分支中。当编译器不确切知道发生了什么时,最安全的做法是缩小真分支而不是缩小假分支。
但我没想到编译器会在品牌原语的情况下采用这条路线。 animal
的类型不可能成为 Opaque<"Dog", "Animal">
一旦你有animal !== "Dog"
.所以我倾向于提交一个关于这个问题的 GitHub 问题,看看他们怎么说;感觉要么是错误,要么至少是设计限制。我有点惊讶我之前没有看到这个问题,而且我找不到已经提交的直接相关问题。那好吧。
那么,有哪些可能的解决方法?一种是制作user-defined type guard function .用户定义的类型保护通常由编译器处理,即使是 false
结果意味着参数的缩小。这并不总是可取的(请参阅 microsoft/#15048 以获取允许此类类型保护函数更具可配置性以便 false
不会缩小返回值的建议),但这正是您想要的。它可以这样实现:
function is<T extends string>(x: any, v: T): x is T {
return x === v;
}
function makeSound(animal: Animal) {
if (is(animal, "Dog")) {
return "Haw!";
} else if (is(animal, "Cat")) {
return "Meow"!
} else {
assertNever(animal); // no error now
}
}
switch
/
case
函数调用语句
if
/
else
陈述,所以它可能太痛苦了。
unique
type brand proposal in microsoft/TypeScript#33038 .但现在我能想到的最简单的解决方法是让您保留
switch
语句是使用
string enum .
enum Animal {
DOG = "Dog",
CAT = "Cat"
}
function makeSound(animal: Animal) {
switch (animal) {
case Animal.DOG:
return "Woof!"; // English-speaking dog
case Animal.CAT:
return "Meow!";
default:
assertNever(animal); // no error
}
}
Opaque
,
animals
, 和
Animal
被单个
Animal
替换枚举。请注意,在
makeSound
我们必须针对
Animal.DOG
进行测试和
Animal.CAT
而不是反对
"Dog"
和
"Cat"
.否则编译器仍然不会进行错误案例缩小。幸运的是,检查枚举值确实有效。
关于javascript - 使用 Typescript 在 Opaque 上区分联合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61297080/
我已经写了并且 npm 发布了这个:https://github.com/justin-calleja/pkg-dependents 现在我正在用 Typescript 编写这个包:https://g
我有一个函数,我想在 TypeScript 中模拟它以进行测试。在我的测试中,我只关心 json和 status .但是,当使用 Jest 的 jest.spyOn 时我的模拟函数的类型设置为返回 h
我正在使用一个库 (Axios),它的包中包含 Typescript 声明。 我想声明一个将 AxiosResponse(在库的 .d.ts 文件中声明)作为参数的函数。我有以下内容: functio
我是 Typescript 的新手。我想使用 将一个 Typescript 文件加载到另一个 Typescript 文件中标签。 我做了一些事情,但它不起作用!请帮助我。 first.ts: imp
为什么我会收到下面屏幕截图中显示的错误? Atom 说我的 tsconfig.json“项目文件包含无效选项”用于 allowJs、buildOnSave 和 compileOnSave。 但是应该允
所以我正在创建一个 TypeScript 库,我可以轻松地将所有生成的 JS 文件编译成一个文件。有没有办法将所有 .ts 和 .d.ts 编译成一个 .ts 文件? 除了支持 JS 的版本(较少的智
Microsoft Research 提供了一种名为Safer TypeScript 的新 TypeScript 编译器变体: http://research.microsoft.com/en-us/
我需要这个来在单个文件中分发 TypeScript 中的库。有没有办法将多个 typescript 文件合并到(一个js文件+一个 typescript 定义)文件中? 最佳答案 要创建一个库,您可以
用例:我想知道一个函数在 typescript 中执行需要多少时间。我想为此目的使用装饰器。我希望装饰器应该返回时间以便(我可以进一步使用它),而不仅仅是打印它。 例如: export functio
我想检查一个类型是否可以为 null,以及它是否具有值的条件类型。 我尝试实现 type IsNullable = T extends null ? true : false; 但是好像不行 type
我的问题是基于这个 question and answer 假设我们有下一个代码: const myFn = (p: { a: (n: number) => T, b: (o: T) => v
我知道双重否定前缀,我知道 TypeScript 的单后缀(非空断言)。 但是这个双后缀感叹号是什么? /.*验证码为(\d{6}).*/.exec(email.body!!)!![1] 取自here
我正在使用以下文件结构在 Webstorm 中开发一个项目 | src | ... | many files | types | SomeInterface |
在 TypeScript 类中,可以为属性声明类型,例如: class className { property: string; }; 如何在对象字面量中声明属性的类型? 我试过下面的代码,但它
我正在寻找一种在不丢失推断类型信息的情况下将 TypeScript 中的文字值限制为特定类型的好方法。 让我们考虑一个类型Named,它保证有一个名字。 type Named = { name:
在 TypeScript 中,我想创建一个联合类型来表示属于一个或多个不同类型的值,类似于 oneOf在 OpenAPI或 JSON Schema .根据a previous answer on a
type Func = (foo:string) => void // function expression const myFunctionExpression:Func = function(f
假设我有一个联合类型,我正在使用类似 reducer 的 API 调用模式,看起来像这样: type Action = { request: { action: "create
我在 typescript 中有以下去抖功能: export function debounce( callback: (...args: any[]) => void, wait: numb
在 Vue3 的 defineComponent 函数中,第一个泛型参数是 Props,所以我在这里使用 Typescript 接口(interface)提供我的 props 类型。喜欢: expor
我是一名优秀的程序员,十分优秀!