在以下示例中:

function fn(_: number | { a: number }) {}


declare const x: number & { a: string };
                            // ^___ it's type string instead of number

fn(x); // no error


declare const y: string & { a: number };
              // ^___ it's type string instead of number

fn(y); // no error

Playground Link

有没有办法防止交叉口类型起作用?或者防止交叉点类型部分不正确?

谢谢.

编辑:

你可以通过做Object.assign(1, { a: 9 })得到交叉口类型.

因为在Java脚本中不存在真正的重载,所以当参数是数字时,这样的函数的目的是做一些事情(即,将它用作索引),而当参数是具有特定类型的属性的记录时,则是另一件事.

为了能够同时对这两种类型的参数进行操作(就像一个Java脚本重载),它需要能够限制交集类型,这就是我问它是否可能的原因.

谢谢.

推荐答案

TypeScrip不想帮助强制执行约束,因为它违反了类型系统的规则,例如:union的每个成员都可以分配给并集(T可以分配给T | U,TU都可以分配给T | U),intersection可以分配给交集的每个成员(T & U可以分配给TU).这意味着接受number | {a: number}的正常函数也应该接受number,因此也应该接受number & {a: string}.同样,它应该接受{a: number},因此也就是string & {a: number}.如果您发现自己想要阻止这样的任务,您可能希望花一些时间判断您的用例,以确保您有一个非常好的理由这样做,因为您将使用打字打字系统.我会假设从现在开始我们确实想要继续进行.


在TypeScrip中,primitive types与对象类型的交集是一种方便的虚构,之所以允许这样做,是因为它们有助于模拟nominal types.请参见the FAQ entry for "Can I make type aliases nominal?".这些被称为"品牌原语".从技术上讲,这种类型是不可能存在的,因此"应该"减少到the never type种.事实是,这种情况没有发生,这意味着我们可以预料到会看到一些奇怪的异常行为.

特别奇怪的是,string & {a: number}被视为既可以赋值给string,也可以赋值给object,尽管string & objectnever.如果您试图表达"Anumber不是object"或"{a: number}不是object",那么对于任何合理的实现,它们最终看起来都只是number{a: number},并且没有任何变化.

我唯一能想到的表达方式就是通过genericsconditional types.我们没有判断,比如说string & object(never),而是判断了类似T extends string ? T extends object ? ⋯ : ⋯ : ⋯的东西.

以下是一种可能的实现:

type Primitive = string | number | bigint | null | undefined | boolean | symbol;
function fn<T extends number | { a: number }>(
    _: T extends object ? T extends Primitive ? number & { a: number } : T : T
) { }

我已经将Primitive定义为所有基元类型的并集,这样我们就可以防止所有(非number)基元类型,而不仅仅是string.支票T extends object ? T extends Primitive ? ⋯ : T : T基本上将仅捕获可分配给Primitiveobject两者的标记基元.然后,一旦我们知道我们有一个标记的原语,我们就可以确保我们只接受您想要接受的原语,即number & {a: number}.

让我们测试一下:

fn(1); // okay
fn("abc"); // error
fn({ a: 9 }); // okay
fn({ a: "abc" }); // error
fn(Object.assign(1, { a: 9 })); // okay
fn(Object.assign("abc", { a: 9 })); // error
fn(Object.assign(1, { a: "abc" }); // error

看上go 不错.

Playground link to code

Typescript相关问答推荐

TypScript如何在 struct 上定义基元类型?

在这种情况下,如何获得类型安全函数的返回值?

有没有办法解决这个问题,类型和IntrinsicAttributes类型没有相同的属性?

路由链接不会导航到指定router-outlet 的延迟加载模块

在包含泛型的类型脚本中引用递归类型A<;-&B

通过字符串索引访问对象';S的值时出现文字脚本错误

避免混淆两种不同的ID类型

如何避免从引用<;字符串&>到引用<;字符串|未定义&>;的不安全赋值?

为什么上下文类型在`fn|curred(Fn)`的联合中不起作用?

TypeScrip-如何自动推断UNION变量的类型

错误:NG02200:找不到类型为';对象';的其他支持对象';[对象对象]';.例如数组

创建一个TypeScrip对象并基于来自输入对象的约束定义其类型

作为函数参数的联合类型的键

对于VUE3,错误XX不存在于从不存在的类型上意味着什么?

两个名称不同的相同打字界面-如何使其干燥/避免重复?

可以将JS文件放在tsconfig';s includes/files属性?或者,我如何让tsc判断TS项目中的JS文件?

确保财产存在

Mongodb TypeError:类继承 events_1.EventEmitter 不是对象或 null

通用可选函数参数返回类型

类型string不可分配给类型string| 联盟| 的'