typescript - 是否有可能检查一个类型是否已经合并了内部类型?
我想检查是否有一个内部类型合并到另一个类型,所以我尝试了:
type A = {
field: number
}
namespace A {
type B = {
field: string
}
}
type C = {
field: boolean
}
// What should I write here?
type HasInnerB<T> = /* ??? */ ? true : false
type AHasInnerB = HasInnerB<A>; // Expected to be true since type A.B exists
type CHasInnerB = HasInnerB<C>; // Expected to be false since type C.B doesn't exist
我不确定这是否可行。
答案#1
目前这是不可能的。TypeScript 中的声明会引入一个命名的类型、一个命名的值或一个命名的命名空间,或者它们的某种组合。类型、值和命名空间的名称实际上彼此之间没有任何内在联系。仅仅因为它们共享一个名称,你不能从一个访问另一个。事实上,你可以有一个名为X的类型,一个名为X的值,以及一个名为X的命名空间,它们在任何程序上都没有关联:
// a namespace named X which exports a type named N
namespace X {
export type N = { n: string };
}
const a: X.N = { n: "" }
// a value named X
const X = { v: "" };
X.v.toUpperCase();
// a type named X
type X = { t: string };
const b: X = { t: "" };
所以就像命名空间、值和类型生活在平行的世界中。一些声明会用相同的名字引入多个这些内容(例如,一个 class 声明会引入一个构造函数的 值 和一个实例的 类型;而一个 namespace 声明可以同时引入一个导出类型的 命名空间 和一个导出变量的 值),但你仍然不能仅凭名字就从一个访问另一个。
请注意,这 不 被视为声明合并。名为 X 的类型和名为 X 的值不会合并为一个实体。声明合并只能在多个声明在同一个作用域中引入同名实体时发生(例如两个名为 X 的 值,或两个名为 X 的 类型,或两个名为 X 的 命名空间)……而且实际上只有部分合并是被允许的(通常你不能将两个值合并,或者两个类型别名合并,等等)。
从问题中不清楚你想以何种方式检查一个任意实体是否有名为 B 的成员,因为这取决于该实体所在的世界,有三种不同的含义。但目前,TypeScript 仅提供通用的 类型 参数和参数。任何形式为 HasInnerB<T> 的工具类型都要求 T 是一个 类型,而不是一个值。
所以如果你写 HasInnerB<A>,这个 A 与名为 A 的命名空间(它导出了一个名为 B 的类型)没有任何关系。因此,不管你如何实现 HasInnerB<T>,它都不会按预期工作。
为了让这一点成为可能,TypeScript 必须添加一些功能,让你可以对值和值空间进行抽象。目前对于命名空间来说完全没有这样的功能,对于值来说,typeof 操作符只能直接作用于某个值,而不能作用于某个“值参数”(所以你可以写 typeof val,但无法写 type TypeOf<T>=typeof T,因为 T 是类型参数,而不是值参数)。
如果实现的话,类似 microsoft/TypeScript#17588 或 microsoft/TypeScript#55521 或 microsoft/TypeScript#56124 的某些功能请求可能会让我们能够编写 HasInnerB<T>,但这可能是一项很大的工作(难的不是基本功能,而是处理它与语言其他部分的交互才是棘手的部分),而且我在 GitHub 上几乎没有看到对这些功能的热情(截至 2024-01-10,其中投票最多的功能只有 54 个 👍,这虽不算少,但离被认为热门还差得远),所以我非常怀疑这会在短时间内发生,不幸的是。
代码的 Playground 链接
