TypeScript 在Map类型的扩展子句中,请使用逻辑运算符“and”、“or”和“not”,

ltskdhd1  于 9个月前  发布在  TypeScript
关注(0)|答案(8)|浏览(96)

搜索词

基本上就是标题里的内容...

建议

我希望能够使用逻辑运算符来表示类型。
我知道 not is already planned as a feature ,但我希望确保这能进一步扩展到其他逻辑运算符,而且我找不到任何迹象表明这已经在你们的脑海中了。

用例

类型组合,提高可读性...

示例

当前实现所需行为的方法:

  1. type Not<T extends boolean> = T extends true ? false : true
  2. type Or<A extends boolean, B extends boolean> = A extends true
  3. ? true
  4. : B extends true
  5. ? true
  6. : false
  7. type Or3<A extends boolean, B extends boolean, C extends boolean> = Or<A, Or<B, C>>
  8. type And<A extends boolean, B extends boolean> = A extends true
  9. ? B extends true
  10. ? true
  11. : false
  12. : false

一些任意的使用场景:

  1. type Primitive = boolean | number | string | symbol | null | undefined | void
  2. type IsA<T, E> = T extends E ? true : false
  3. type IsIndexSignature<P> = Or<IsA<string, P>, IsA<number, P>>
  4. type IsCallback<F extends Function> = F extends (...args: any[]) => any
  5. ? And<Not<IsA<Parameters<F>[0], Primitive>>, IsA<Parameters<F>[0], Event>> extends true
  6. ? true
  7. : false
  8. : false

在 Playground 中全部展示:这里
想要用相同的语法写出相同的东西:

  1. type IsIndexSignature<P> = IsA<string, P> or IsA<number, P>
  2. type IsCallback<F extends Function> = F extends (...args: any[]) => any
  3. ? not IsA<Parameters<F>[0], Primitive> and IsA<Parameters<F>[0], Event> extends true
  4. ? true
  5. : false
  6. : false

最好能配合更好的布尔类型声明支持。也就是说,允许直接使用三元运算符,而不需要在每个地方都频繁地使用 extends true 。也许可以引入一种新的“布尔类型声明”,以避免需要在整个过程中传播布尔值。例如,应该可以像这样定义 KnownKeys (不是完整的实现):

  1. type KnownKeys<T> = {
  2. [P in keyof T]: IsIndexSignature<P> ? never : T[P]
  3. }

而不需要这样做:

  1. type KnownKeys<T> = {
  2. [P in keyof T]: IsIndexSignature<P> extends true ? never : T[P]
  3. }

检查清单

我的建议满足以下准则:

  • 这不会对现有的 TypeScript/JavaScript 代码造成破坏性的改变
  • 这不会改变现有 JavaScript 代码的运行时行为
  • 这可以在不根据表达式的类型生成不同的 JS 的情况下实现
  • 这不是一个运行时特性(例如库功能、JavaScript 输出的非 ECMAScript 语法等)
  • 这个特性会与 TypeScript's Design Goals 的其他部分保持一致。
hts6caw3

hts6caw31#

我认为为已经与现有工具良好配合的事物引入更多关键字和额外语法并不值得。

vqlkdk9b

vqlkdk9b2#

我认为为已经与现有工具良好配合的东西引入更多关键词和额外语法并不值得。
然而它并不奏效... 难以编写,难以阅读,容易出错,同时又是相当基础的东西,项目会不断重复。

pjngdqdw

pjngdqdw3#

我基本上已经切换到使用 unknownnever 代替 truefalse 进行这种操作。它还有一个额外的好处,就是让你可以使用 & 作为 "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

i2byvkas

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 using never and unknown .
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.

fae0ux8s

fae0ux8s5#

我认为“hack”是见仁见智的,在这里。虽然布尔字面量类型 truefalse 有富有表现力的名字,但它们只表示在运行时存在的布尔 表达式 类型,它们并不像布尔 在类型级别 上的行为。
另一方面,unknownnever 是 TypeScript 对 topbottom 类型的名称,它们在类型级别上的行为更像布尔值,这就是你所要求的。
这种对应关系来自于将一个类型等同于可以分配给该类型的某个值的语句:
| 命题逻辑 | 类型系统 | 对应关系 |
| ------------ | ------------ | ------------ |
| T/true/⊤ | unknown (顶层) | 可以分配给 unknown? true。 |
| F/false/⊥ | never (底层) | 可以分配给 never? false。 |
| and/∧ | & (交集) | 可以分配给 A & B 当且仅当它可以分配给 AandB 。 |
| or/∨ | | (并集) | 可以分配给 A | B 当且仅当它可以分配给 either AorB 。 |
| not/¬ | not *(补集) | 可以分配给 not A 当且仅当它不是 not to A 。 |

希望这看起来不那么随意了。当然,目前还没有 not ,而且命题逻辑通常没有三元运算符的基本符号,所以我们需要我们自己的 Not<T>Cond<T,Y,N> ,正如我之前所说:
那么,让我们看看你的示例:

  1. type Primitive = boolean | number | string | symbol | null | undefined | void
  2. type IsA<T, E> = T extends E ? unknown : never;
  3. type Cond<C, Y=unknown, N=never> = [C] extends [never] ? N : Y
  4. type IsIndexSignature<P> = IsA<string, P> | IsA<number, P>
  5. type Yes = IsIndexSignature<number> // (never | unknown) = unknown
  6. type No = IsIndexSignature<boolean> // (never | never) = never

这样是否更有意义?

展开查看全部
0mkxixxg

0mkxixxg6#

嗯...有点。我尝试用更大的例子替换 true 的几乎每一个出现,用 unknown 替换 false ,用 never 替换 Or<T> ,用 | 替换等等,但我失败了。问题是,以前 IsIndexSignature<T> 可以通过 extends true ? 轻松地用于更大的类型测试。现在这种灵活性丧失了,我认为被迫使用 extends never ? (或 extends [never] ??我真的不明白为什么要用数组 Package ...),因为一切都扩展自 unknown ,因此反转所有条件(相当多)。
底线是,我不认为你的建议是一个合理的建议。黑客并不完全取决于观察者的看法,我想说。这种技巧的安排比“常规二进制逻辑”更难理解--在我看来,痛苦太高,犯错更容易。

mcdcgff0

mcdcgff07#

我不太明白为什么要用数组 Package ...
如果你指的是这个模式:[T] extends [U] ? Y : N - 它是为了关闭union distribution(参见分布式条件类型)。泛型条件类型Map到联合的各个成员,所以如果我们不希望这种情况发生,我们将类型的联合变成一个“联合元组”,将其视为一个原子类型。你可以替换[T],它几乎可以替代任何其他协变泛型,如Promise<T>,效果是一样的。

jhiyze9q

jhiyze9q8#

这是一段关于TypeScript Handbook中条件类型的描述:

条件类型根据一个表示为类型关系测试的条件选择两种可能的类型之一:

  1. T extends U ? X : Y

上面的类型意味着当T可以分配给U时,类型是X,否则类型是Y。
(来自https://www.typescriptlang.org/docs/handbook/advanced-types.html#conditional-types)
它从未说过条件类型会解析为 truefalse ,所以如果我们想明确条件的结果,任何二进制值都可以工作, unknownnever 肯定是可以的。

但是 true / false 就不行了。
每个人都知道在运行时,true 的相反是 false ,反之亦然(当然除了NaN)。
但在否定类型中,not true 的意思是“任何不能分配给 true 的东西”,而 not false 的意思是“任何不能分配给 false 的东西”。
所以例如 string 同时是 not truenot false,那么 string ? true: false 将解析为什么?
然而我不得不承认,使用 unknownnever 的方式要少直观得多。
最初我来这里是因为我发现了一个非常酷的 blog post (中文),它仅使用类型系统构建脚本解析器。

(Playground链接)
为了链接多个表达式,作者必须大量使用条件类型。所以我在想是否有任何提案将逻辑运算符添加到条件类型中,就像我们在JavaScript中可以使用 && 一样。

展开查看全部

相关问题