Typescript(真的)是不是遵循泛型中参数化类型(T、U、V、W)的命名约定?
Posted
技术标签:
【中文标题】Typescript(真的)是不是遵循泛型中参数化类型(T、U、V、W)的命名约定?【英文标题】:Does Typescript (really) follows the naming convention for parameterized types (T, U, V, W) in Generics?Typescript(真的)是否遵循泛型中参数化类型(T、U、V、W)的命名约定? 【发布时间】:2021-05-25 06:01:45 【问题描述】:我不确定在 TS 中我们是否遵循 C++/Java 或许多其他语言 (T,U,V,W
) 等参数化类型的命名约定。
我多次看到 TS 中参数化类型约定的混合使用。 比如在release notes of TS 2.8:
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
为什么是R
而不是U
?
那么,在下一个例子中:
type Unpacked<T> = T extends (infer U)[]
? U
: T extends (...args: any[]) => infer U
? U
: T extends Promise<infer U>
? U
: T;
为什么是U
而不是R
?
另一个例子,在上面代码的同一个代码块中:
type T0 = Unpacked<string>; // string
type T1 = Unpacked<string[]>; // string
type T2 = Unpacked<() => string>; // string
type T3 = Unpacked<Promise<string>>; // string
type T4 = Unpacked<Promise<string>[]>; // Promise<string>
type T5 = Unpacked<Unpacked<Promise<string>[]>>; // string
但在TS源代码中:
// We represent tuple types as type references to synthesized generic interface types created by
// this function. The types are of the form:
//
// interface Tuple<T0, T1, T2, ...> extends Array<T0 | T1 | T2 | ...> 0: T0, 1: T1, 2: T2, ...
以上问题只是说明问题的示例,您无需回答。
我可以添加一些其他示例,其中 U
有时被视为返回类型,有时只是第二种类型。
我真正的问题是:我们(真的)关注Java Generic Types convention 的 TS Generics 吗? 如果不是,TS 对泛型遵循什么样的约定? TS 中同时存在多少种不同的类型约定(除了类型名称的明显 PascalCase 和类型属性/方法的 camelCase)?
【问题讨论】:
我真的看不出这些示例与您链接的 Java 约定有何有意义的不同:使用少量大写字符,一些常用名称对应于它们所代表的第一个字母(K
用于键,P
用于属性,R
用于返回等)或一些相关类型的序列,例如 T
,U
,V
或 A
,B
,C
.不过,我不确定您的问题是否有明确的答案......像这样的约定在某种程度上是有机的/众包的,而不是从高处颁布的。
非常感谢您的评论。所以我们可以得出结论,只要我保留一个(有时)描述性(有时)随机大写的单个字母,我就遵循 Typescript 约定?与我链接的 Java 约定不完全相同。例如,在 Java 中,我必须使用 T
,U
,V
来抱怨多种类型泛型的类型参数命名约定。在 TS 中我可以使用 T
, U
, V
或 A
,B
,C
或 X
,Y
,Z
或 @987654376@,@, 987654358@随机互换。因此,我想我可以肯定地说,它可能起源于 C++/Java,但不那么严格。
链接的文档说“最常用的类型参数名称是……”而不是“唯一可接受的类型参数名称是……”。它是 examples 的列表,因此是描述性的而不是规定性的。抱歉,我真的看不出有什么是“不那么严格”或“更严格”的。
这个问题有没有可能只用意见来回答?我试图弄清楚一个规范的答案会是什么样子。有像ms/TS#878 和ms/TS#6168 这样的GitHub 问题,TS 设计团队说“我们不打算将任何此类约定强加给其他人”,因此从这个意义上说,答案是“TS 中没有规定性约定”。 ESLint 或 TSLint 似乎也没有推荐的类型参数设置。
我同意你的所有观点。我试图让它尽可能不基于选项(甚至不想添加诸如:更具描述性的命名约定怎么样?)。但我们正在接近基于意见的答案。我认为您的 cmets 可能是我问题的一个非常好的答案,它可能对其他人有用。取决于您,您有更多的 SO 经验,我们可以根据意见投票结束问题,或者您可以复制您的 cmets 作为答案。如果你投票支持关闭,我也会这样做。非常感谢。
【参考方案1】:
(主要来自 cmets)
我能想到的最接近的回答这个不仅仅是我的意见是指向一些描述“TypeScript 类型参数命名约定”是什么的文档,并将其与您链接的Java Generics tutorial document 进行比较。
如果是这样,TypeScript 设计团队对此的官方立场似乎是“我们不打算将任何此类约定强加给其他人”,或者“TS 中没有规范的命名约定”。见microsoft/TypeScript#6168 和microsoft/TypeScript#878,特别是this comment:
[I]一般来说,我们对为人们决定风格上的东西不感兴趣。说 TS 有一种认可的样式违背了我们的理念,即我们在这里为 JS 提供类型,无论您如何编写它(当然是在合理的参数范围内 ?)。
还有ESLint's naming-convention
rule 和TSLint's naming-convention
rule,这使得可以在经过 linter 检查的代码库中强制执行类型参数命名约定,但默认情况下似乎不这样做 /em>。因此,似乎没有任何约定足够正式,可以默认强制执行。
为了比较起见,让我们看一下您链接的 Java 泛型教程文档中的相关部分:
类型参数命名约定
按照惯例,类型参数名称是单个大写字母。这与您已经知道的变量命名约定形成鲜明对比,并且有充分的理由:如果没有这种约定,就很难区分类型变量和普通类或接口名称。
最常用的类型参数名称有:
E - 元素(Java 集合框架广泛使用) K - 键 N - 编号 T - 类型 V - 值 S、U、V 等 - 第 2、第 3、第 4 类您将在整个 Java SE API 和本课的其余部分中看到这些名称。
请注意,上面列举的列表被描述为“最常用的类型参数名称”,而不是“唯一允许的类型参数名称”;这是一个列表 示例,因此 descriptive 而不是 prescriptive。
关于选择“单个,大写字母”的部分更接近处方:大写字母倾向于区分类型名称和变量名称和单字符类型名称倾向于区分类型参数和特定类型,如类或接口。但我有同样的感觉,这与其说是上层的法令,不如说是对普遍做法的观察。
所以我们大概可以停在那里说“TypeScript 或 Java 中没有官方或规范的类型参数命名约定,任何非官方的此类约定都是见仁见智的问题。”
但为了尝试列出我认为 TypeScript 中的非官方约定实际上是,我会继续。请记住,这是我的意见,人们可能会不同意。
我想说,上面列出的 Java 命名约定与我认为的 TypeScript 泛型类型参数的实际命名约定非常接近:使用单个大写字符,或者对应于它们的第一个字母表示,如:
T
代表“类型”,最通用也是最常用的类型参数名称;
K
表示“key”,或P
表示“property”,这两者都倾向于受到PropertyKey
或keyof T
或keyof SomeInterface
或keyof SomeClass
的约束;
V
表示“值”,最常与K
表示“键”成对使用;
A
for "arguments" and R
for "return",分别对应函数签名的其余参数列表和返回类型,如(...args: A) => R
;
N
用于“数字”,S
用于“字符串”,B
用于“布尔值”,用于受基元约束的类型参数;
或一些相关类型的序列,例如:
T
、U
、V
、W
等,从 T
开始表示“类型”,然后在需要更多类型时遍历字母表,请记住,你只能得到几个这个方式;
A
、B
、C
、D
等,从字母表的开头开始,当您希望使用一大堆类型参数并且您还没有将类型参数用于其他用途时。
这样的约定不是绝对的,并且会在必要时进行弯曲以避免歧义或其他混淆。如果您需要的类型参数多于上面没有名称冲突的情况,则可能需要在名称中添加一个字符:
T0
、T1
、T2
、T3
等,附加数字得到相关类型的序列;
KT
、KU
、KV
:分别为 T
、U
和 V
的“键”添加前缀 K
;
这与我认为的传统方式相去甚远,但仍然足够普遍,可以编写短的 UpperCamelCase 名称来更好地描述类型所代表的内容,缺点是它们可能会开始混淆特定类型而不是类型参数:
Key
, Val
, Prop
, Arg
, Ret
, Type
, This
以下是非常规的(记住,我的意见在这里!)应该避免,除非有一些压倒性的情有可原的理由这样做:
类似于接口或类名的长名称,例如InputType
或Properties
;
在较长的类型名称(如 TNotRecommended
)上添加大写 T
前缀;
以小写字母开头的名称,例如 t
或 u
或 myType
;
【讨论】:
以上是关于Typescript(真的)是不是遵循泛型中参数化类型(T、U、V、W)的命名约定?的主要内容,如果未能解决你的问题,请参考以下文章