4

I am not exactly sure if in TS we follow this naming convention for parameterized types as C++/Java or many other languages (T,U,V,W).

I saw many times a mixed usage of parameterized types conventions in TS. For example, in the release notes of TS 2.8:

type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;

Why R and not U?

then, in the next example:

type Unpacked<T> = T extends (infer U)[]
  ? U
  : T extends (...args: any[]) => infer U
  ? U
  : T extends Promise<infer U>
  ? U
  : T;

Why U and not R?

Another example, in the same code block of the code just above:

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

But in the TS source code:

// 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, ... }

The questions above are just examples to make a point, you don't need to answer them. I could add some other examples where U is treated sometimes like the return type and sometimes is just a second type.

My real question is: are we (really) following Java Generic Types convention for TS Generics? If not, what kind of convention(s) does TS follows for Generics? How many different Type conventions are co-existing in TS (other than the obvious PascalCase for Type name and camelCase for type properties/methods)?

jcalz
  • 264,269
  • 27
  • 359
  • 360
GBra 4.669
  • 1,512
  • 3
  • 11
  • 23
  • 1
    I don’t really see how these examples differ in any meaningful way from the Java convention you linked: use few, uppercase characters, with some common names corresponding to either the first letter of what they represent (`K` for key, `P` for property, `R` for return, etc) or some sequence of related types like `T`,`U`,`V` or `A`,`B`,`C`. I’m not sure there’s a definitive answer to your question though... conventions like this are somewhat organic/crowd-sourced and not decreed from on high. – jcalz Feb 23 '21 at 00:12
  • Thanks a lot for your comment. So we can conclude that as long as I keep a (sometimes)-descriptive (sometimes)-random capitalized single letter, I am following the Typescript convention? Is not exactly the same as the Java convention I linked. For example, in Java, I would have to use `T`,`U`,`V` to be complaint with Type Parameter Naming Conventions on multiple types generics. In TS I can use `T`, `U`, `V` or `A`,`B`,`C` or `X`,`Y`,`Z` or `T0`, `T1`, `T2` randomly and interchangibly. Hence, I think I can safely say that is probably originated from C++/Java but it's less strict. – GBra 4.669 Feb 23 '21 at 09:12
  • 1
    The linked document says “The most commonly used type parameter names are...” and not “The only acceptable type parameter names are...”. It’s a list of *examples* and is thus descriptive and not prescriptive. I’m really not seeing how anything is “less strict” or “more strict”, sorry. – jcalz Feb 23 '21 at 12:07
  • Is it possible that this question could be answered with anything but opinion? I'm trying to figure out what a canonical answer could look like. There are GitHub issues like [ms/TS#878](https://github.com/microsoft/TypeScript/issues/878) and [ms/TS#6168](https://github.com/microsoft/TypeScript/issues/6168) where the TS design team says "we do not intend to impose any such conventions on others", so in that sense the answer is "there are no prescriptive conventions in TS". It doesn't appear that ESLint or TSLint has a recommended setting for type parameters either. – jcalz Feb 23 '21 at 14:52
  • And since that Java doc link seems (in my opinion) to be suggesting single-character uppercase types along with examples, and not prescribing or proscribing anything in particular, it's not clear there's much to adhere to. In my experience and opinion, TS generic type parameters tend to be and should be uppercase single letters where unambiguous, possibly longer (two characters? a short word?) to resolve ambiguity. But that's my opinion, and unsourced "experience", so I wouldn't feel great making that an answer. I think maybe this is an opinion question, but maybe I'm misinterpreting it. – jcalz Feb 23 '21 at 14:56
  • 1
    I agree with you on all your points. I tried to keep it as non opionion-based as possible (didn't even wanted to add things like: what about a more descriptive naming conventions?). But we are getting close to an opinion based answer. I think that your comments could be a really nice answer to my question and it might be useful for others. Up to you, you have more SO experiece, we can vote to close the question as opinion-based or you can copy your comments as an answer. If you vote for closing, I will do it too. Thank you so much. – GBra 4.669 Feb 23 '21 at 15:31

1 Answers1

3

(From the comments, mostly)

The closest I can imagine answering this in any way which is not just my opinion would be to point to some documentation that describes what the "TypeScript Type Parameter Naming Convention" is and compare it to the Java Generics tutorial document you linked.

If so, the TypeScript design team's official stance on this seems to be "we do not intend to impose any such conventions on others", or "there are no canonical naming conventions in TS". See microsoft/TypeScript#6168 and microsoft/TypeScript#878, specifically this comment:

[I]n general we're not interested in deciding stylistic things for people. Saying that there's One Approved Style for TS goes against our philosophy that we're here to provide types for JS regardless of how you're writing it (within reasonable parameters, of course ).

There's also ESLint's naming-convention rule and TSLint's naming-convention rule, which make it possible to enforce type parameter naming conventions in a linter-checked code base, but do not seem to do by default. So no convention seems to be official enough to be enforced by default.


For comparison's sake, let's take a look at the relevant section from the Java Generics tutorial document you linked:

Type Parameter Naming Conventions

By convention, type parameter names are single, uppercase letters. This stands in sharp contrast to the variable naming conventions that you already know about, and with good reason: Without this convention, it would be difficult to tell the difference between a type variable and an ordinary class or interface name.

The most commonly used type parameter names are:

  • E - Element (used extensively by the Java Collections Framework)
  • K - Key
  • N - Number
  • T - Type
  • V - Value
  • S,U,V etc. - 2nd, 3rd, 4th types

You'll see these names used throughout the Java SE API and the rest of this lesson.

Note that the enumerated list above is described as "the most commonly used type parameter names" and not "the only allowable type parameter names"; it is a list of examples and thus descriptive and not prescriptive.

The part that about choosing "single, uppercase letters" is closer to a prescription: uppercase letters tend to distinguish type names from variable names, and single-character type names tend to distinguish type parameters from specific types like class or interfaces. But I get the same sense that this is not so much a decree from on high but an observation about common practices.


So we could presumably stop there and say "there is no official or canonical type parameter naming convention in either TypeScript or Java, and any unofficial such convention is a matter of opinion."

But for the sake of trying to list out what I think the unofficial convention in TypeScript actually is, I'll go on. Keep in mind that it's my opinion and people could plausibly disagree.


I'd say that the Java naming convention laid out above aligns pretty closely with what I would consider the de facto naming convention for TypeScript generic type parameters: use a single uppercase character, either corresponding to either the first letter of what they represent, such as:

  • T for "type", the most general and therefore the most commonly used type parameter name;
  • K for "key", or P for "property", both of which tend to be constrained by PropertyKey or keyof T or keyof SomeInterface or keyof SomeClass;
  • V for "value", most commonly used as a pair with K for "key";
  • A for "arguments" and R for "return", corresponding to the rest parameter list and return type of function signatures respectively, like (...args: A) => R;
  • N for "number", S for "string", B for "boolean, for type parameters constrained by primitives;

or some sequence of related types such as:

  • T, U, V, W, etc., starting with T for "type" and then walking through the alphabet when needing more types, keeping in mind that you can only get a few this way;
  • A, B, C, D, etc., starting from the beginning of the alphabet when you expect to use a whole bunch of type parameters and you haven't already used type parameters for something else.

Such conventions are not absolute, and will tend to be bent where necessary to avoid ambiguity or other confusion. If you need more type parameters than can be obtained above without name collisions, it might be desirable to add a character to the name:

  • T0, T1, T2, T3, etc., appending numbers to get a sequence of related types;
  • KT, KU, KV: prefixing K for "keys of" T, U, and V, respectively;

It is farther from what I consider conventional but still common enough to write short UpperCamelCase names to be more descriptive of what the types represent, with the drawback that they could start being confused for specific types and not type parameters:

  • Key, Val, Prop, Arg, Ret, Type, This

The following is unconventional (remember, my opinion here!) and should be avoided unless there is some overwhelming extenuating reason to do so:

  • Long names that look like interface or class names like InputType or Properties;
  • Prefixing an uppercase T on a longer type name like TNotRecommended;
  • Names that begin with a lowercase letter like t or u or myType;
jcalz
  • 264,269
  • 27
  • 359
  • 360