搜索词
基本上就是标题里的内容...
建议
我希望能够使用逻辑运算符来表示类型。
我知道 not
is already planned as a feature ,但我希望确保这能进一步扩展到其他逻辑运算符,而且我找不到任何迹象表明这已经在你们的脑海中了。
用例
类型组合,提高可读性...
示例
当前实现所需行为的方法:
type Not<T extends boolean> = T extends true ? false : true
type Or<A extends boolean, B extends boolean> = A extends true
? true
: B extends true
? true
: false
type Or3<A extends boolean, B extends boolean, C extends boolean> = Or<A, Or<B, C>>
type And<A extends boolean, B extends boolean> = A extends true
? B extends true
? true
: false
: false
一些任意的使用场景:
type Primitive = boolean | number | string | symbol | null | undefined | void
type IsA<T, E> = T extends E ? true : false
type IsIndexSignature<P> = Or<IsA<string, P>, IsA<number, P>>
type IsCallback<F extends Function> = F extends (...args: any[]) => any
? And<Not<IsA<Parameters<F>[0], Primitive>>, IsA<Parameters<F>[0], Event>> extends true
? true
: false
: false
在 Playground 中全部展示:这里
想要用相同的语法写出相同的东西:
type IsIndexSignature<P> = IsA<string, P> or IsA<number, P>
type IsCallback<F extends Function> = F extends (...args: any[]) => any
? not IsA<Parameters<F>[0], Primitive> and IsA<Parameters<F>[0], Event> extends true
? true
: false
: false
最好能配合更好的布尔类型声明支持。也就是说,允许直接使用三元运算符,而不需要在每个地方都频繁地使用 extends true
。也许可以引入一种新的“布尔类型声明”,以避免需要在整个过程中传播布尔值。例如,应该可以像这样定义 KnownKeys (不是完整的实现):
type KnownKeys<T> = {
[P in keyof T]: IsIndexSignature<P> ? never : T[P]
}
而不需要这样做:
type KnownKeys<T> = {
[P in keyof T]: IsIndexSignature<P> extends true ? never : T[P]
}
检查清单
我的建议满足以下准则:
- 这不会对现有的 TypeScript/JavaScript 代码造成破坏性的改变
- 这不会改变现有 JavaScript 代码的运行时行为
- 这可以在不根据表达式的类型生成不同的 JS 的情况下实现
- 这不是一个运行时特性(例如库功能、JavaScript 输出的非 ECMAScript 语法等)
- 这个特性会与 TypeScript's Design Goals 的其他部分保持一致。
8条答案
按热度按时间hts6caw31#
我认为为已经与现有工具良好配合的事物引入更多关键字和额外语法并不值得。
vqlkdk9b2#
我认为为已经与现有工具良好配合的东西引入更多关键词和额外语法并不值得。
然而它并不奏效... 难以编写,难以阅读,容易出错,同时又是相当基础的东西,项目会不断重复。
pjngdqdw3#
我基本上已经切换到使用
unknown
和never
代替true
和false
进行这种操作。它还有一个额外的好处,就是让你可以使用&
作为 "and",使用|
作为 "or"。如果not
被引入,你可以使用它;目前你需要一个通用的类型别名,如type Not<T> = [T] extends [never] ? unknown : never
或可能type Not<T> = unknown extends T ? never : unknown
(取决于你想看到Not<123>
发生什么)。在那之后,唯一缺少的就是缩写的条件类型语法
C ? Y : N
,它代表的是类似于[C] extends [never] ? N : Y
或可能unknown extends C ? Y : N
(取决于你想看到123 ? Y : N
发生什么)的东西。我不指望它能被纳入语言中,所以我想在那里你能做的最好的事情就是使用一个通用的类型别名:type Cond<C, Y=unknown, N=never> = [C] extends [never] ? N : Y
。i2byvkas4#
@jcalz could you illustrate your ideas by applying them to the examples above? For instance, to
IsIndexSignature
, perhaps explaining it a bit? It is quite tricky to become certain that|
will equate to a logical or by somehow usingnever
andunknown
.I am curious, because I want to have such a feature today if this is possible, but it is most definitely a hack that I don't wish anybody to be forced to use for what would have otherwise been a very easy task.
fae0ux8s5#
我认为“hack”是见仁见智的,在这里。虽然布尔字面量类型
true
和false
有富有表现力的名字,但它们只表示在运行时存在的布尔 表达式 类型,它们并不像布尔 在类型级别 上的行为。另一方面,
unknown
和never
是 TypeScript 对 top 和 bottom 类型的名称,它们在类型级别上的行为更像布尔值,这就是你所要求的。这种对应关系来自于将一个类型等同于可以分配给该类型的某个值的语句:
| 命题逻辑 | 类型系统 | 对应关系 |
| ------------ | ------------ | ------------ |
| T/true/⊤ |
unknown
(顶层) | 可以分配给unknown
? true。 || F/false/⊥ |
never
(底层) | 可以分配给never
? false。 || and/∧ |
&
(交集) | 可以分配给A & B
当且仅当它可以分配给A
andB
。 || or/∨ |
|
(并集) | 可以分配给A | B
当且仅当它可以分配给 eitherA
orB
。 || not/¬ |
not
*(补集) | 可以分配给not A
当且仅当它不是 not toA
。 |希望这看起来不那么随意了。当然,目前还没有
not
,而且命题逻辑通常没有三元运算符的基本符号,所以我们需要我们自己的Not<T>
和Cond<T,Y,N>
,正如我之前所说:那么,让我们看看你的示例:
这样是否更有意义?
0mkxixxg6#
嗯...有点。我尝试用更大的例子替换
true
的几乎每一个出现,用unknown
替换false
,用never
替换Or<T>
,用|
替换等等,但我失败了。问题是,以前IsIndexSignature<T>
可以通过extends true ?
轻松地用于更大的类型测试。现在这种灵活性丧失了,我认为被迫使用extends never ?
(或extends [never] ?
?我真的不明白为什么要用数组 Package ...),因为一切都扩展自unknown
,因此反转所有条件(相当多)。底线是,我不认为你的建议是一个合理的建议。黑客并不完全取决于观察者的看法,我想说。这种技巧的安排比“常规二进制逻辑”更难理解--在我看来,痛苦太高,犯错更容易。
mcdcgff07#
我不太明白为什么要用数组 Package ...
如果你指的是这个模式:
[T] extends [U] ? Y : N
- 它是为了关闭union distribution(参见分布式条件类型)。泛型条件类型Map到联合的各个成员,所以如果我们不希望这种情况发生,我们将类型的联合变成一个“联合元组”,将其视为一个原子类型。你可以替换[T]
,它几乎可以替代任何其他协变泛型,如Promise<T>
,效果是一样的。jhiyze9q8#
这是一段关于TypeScript Handbook中条件类型的描述:
条件类型根据一个表示为类型关系测试的条件选择两种可能的类型之一:
上面的类型意味着当T可以分配给U时,类型是X,否则类型是Y。
(来自https://www.typescriptlang.org/docs/handbook/advanced-types.html#conditional-types)
它从未说过条件类型会解析为
true
或false
,所以如果我们想明确条件的结果,任何二进制值都可以工作,unknown
和never
肯定是可以的。但是
true
/false
就不行了。每个人都知道在运行时,
true
的相反是false
,反之亦然(当然除了NaN)。但在否定类型中,
not true
的意思是“任何不能分配给true
的东西”,而not false
的意思是“任何不能分配给false
的东西”。所以例如
string
同时是not true
和not false
,那么string ? true: false
将解析为什么?然而我不得不承认,使用
unknown
和never
的方式要少直观得多。最初我来这里是因为我发现了一个非常酷的 blog post (中文),它仅使用类型系统构建脚本解析器。
(Playground链接)
为了链接多个表达式,作者必须大量使用条件类型。所以我在想是否有任何提案将逻辑运算符添加到条件类型中,就像我们在JavaScript中可以使用
&&
一样。