Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Typescript what does <T> mean?

Tags:

typescript

export declare function createEntityAdapter<T>(options?: {     selectId?: IdSelector<T>;     sortComparer?: false | Comparer<T>; }): EntityAdapter<T>; 

Can someone explain to me what the means? I know <> is type assertion but I don't know what 'T' is. It'd also be helpful if someone could explain to me what this function is doing.

like image 356
Snorlax Avatar asked Apr 03 '18 04:04

Snorlax


People also ask

What is T number in TypeScript?

In the case of an index signature as we see above, we can use T[number] to refer to the type of that index signature—in the case of ArrayMaybe , that is Element . Which is precisely how it's being used in your example. You can't use T[string] because Array doesn't have a string index signature.

Where T is a type variable?

A Generic Version of the Box Class This introduces the type variable, T, that can be used anywhere inside the class. As you can see, all occurrences of Object are replaced by T. A type variable can be any non-primitive type you specify: any class type, any interface type, any array type, or even another type variable.

What is in <> TypeScript?

TypeScript is JavaScript with syntax for types. TypeScript is a strongly typed programming language that builds on JavaScript, giving you better tooling at any scale.

What are generics in TypeScript?

Generics allow creating 'type variables' which can be used to create classes, functions & type aliases that don't need to explicitly define the types that they use. Generics makes it easier to write reusable code.


2 Answers

Can someone explain to me what <T> the means?

That is typescripts Generics declaration.

Excerpt:

A major part of software engineering is building components that not only have well-defined and consistent APIs, but are also reusable. Components that are capable of working on the data of today as well as the data of tomorrow will give you the most flexible capabilities for building up large software systems.

In languages like C# and Java, one of the main tools in the toolbox for creating reusable components is generics, that is, being able to create a component that can work over a variety of types rather than a single one. This allows users to consume these components and use their own types.

You mentioned:

I don't know what 'T' is.

'T' is going to be a type declared at run-time instead of compile time. The T variable could be any non-declared variable (I couldn't find a reference, but I would assume any valid set of characters that could be used for a variable names). Similarly in c#, if the type T represents is not a value type but a more complex type (class) or interface, it could be named/declared as TVehicle or TAnimal to help denote a valid type for future programmers (and could be considered best practice because just T is not intuitive). I prefer TSomething because I know that uppercase T means a generic type. WSometing or ASomething is also valid, but I just don't prefer it. (Microsofts APIs are almost always TContext or TEntity for example).

It'd also be helpful if someone could explain to me what this function is doing.

Well the function isn't doing anything. This is more declaring a type of function that can have multiple run-time type values. Instead of explaining that, I'll include an excerpt taken directly from the link above.

function identity<T>(arg: T): T {   return arg; } 

which can be used like:

// type of output will be 'string' let output = identity<string>("myString");   

or

// type of output will be 'string', the compiler will figure out `T` // based on the value passed in let output = identity("myString");   

or

// type of output will be 'number' let output = identity(8675309);   

Which might beg the question:

Why use generics

Javascript has arrays, but when you retrieve a value from the array, it literally could be anything (typescript: any). With typescript you get Type safety by declaring them like:

 // Array<T>  let list: number[] = [1, 2, 3];  // or   let list: Array<number> = [1, 2, 3]; 

Now each value in the array has a type. Typescript will throw a compile-time error if you attempt to put a string into this array. And you get type-safety and intellisense (depending on your editor) when you retrieve a value:

class Person {   FirstName: string; }  let people: Array<Person> = []; people.push({ FirstName: "John" } as Person);  let john = people.pop();   // john is of type Person, the typescript compiler knows this // because we've declared the people variable as an array of Person  console.log(john.FirstName);   

Declaring type'd generic constraints. A very good example of Open - Closed Principle.

In object-oriented programming, the open/closed principle states "software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification";[1] that is, such an entity can allow its behaviour to be extended without modifying its source code.

In the following example, anyone could extend Human or Cheetah or even create their own derived type and the Logger functionality would continue to work without any modification.

interface IAnimal {   LegCount: number; }  class Cheetah    implements IAnimal {   LegCount: number = 4; }  class Human   implements IAnimal {   LegCount: number = 2; }  public class Logger<TAnimal extends IAnimal> {   public Log(animal: TAnimal) {     console.log(animal.LegCount);   } }   var logger = new Logger(); var human = new Human(); logger.Log(human);       

Working Example

In the previous example I used a Generic Constraint to limit the TAnimal type programmers can use to create a Logger instance to types that derive from the interface IAnimal. This allows the compiler to validate that the Logger class always assume the type has a property LegCount.

You can explain to me why in the Typescript documentation they put <T> instead of putting something more descriptive like <Identity> for example. Nothing and <T> for me is the same. Now does everyone use the <T> like fools, or did I miss something?

These are all going to be assumptions in the following. I do not know neither the team who designed the typescript generic system nor the team who wrote the documentation.

At the root level of generics is the ability to use T as any possible type (not to be confused with typescript any). Meaning Array<T> is the interface (for lack of a better word) and when we create a concrete type we replace T with a declared type:

Array<number> 

So for the interface Array<T> what makes more sense than T? I don't know. I do know that T has to be a Type (number, string, etc) so it makes sense to use T because it the first letter of the word Type. I think Array<Type> would be really confusing and/or might even be invalid if type or Type became reserved or restricted (currently type has special meaning in certain contexts so it's also a poor choice) so avoiding those is a good choice. Other languages (C-sharp, Java) also choose to use T, so switching between languages and being able to use the same term is advantageous.

On the flip side what would the following mean?

Array<Identity> 

What is Identity here? There is no constraint to help other developers or future developers know what it is. It would appear to me to be a specific typed Array that I must implement explicitly, which means it's not up to me to choose the generic type.

interface Foo1 {   bars: Array<Identity>; } 

In the previous example, I (and probably most developers) would assume that Identity is an existing type and I cannot change it.

interface Foo2<T> {   bars: Array<T>; } 

With Foo2 I know I have to choose a type.

interface Foo3<Identity> {   bars: Array<Identity>; } 

Foo3 is just confusing.

interface Foo4<TIdentity> {   bars: Array<TIdentity>; } 

Now with Foo4, I am much more confident that I must choose the type, but I'm still a bit confused why TIdentity. Obviously in some contexts, where the type is more defined, it would make sense.

Edit: As of March 2021, the Typescript Documentation has been updated to deprecate the use of <T> in documentation, in favor instead of <Type>.

That being the case, I personally find the following confusing and would highly suggest avoiding Type it for two reasons.

interface Lengthwise {   length: number; }  function loggingIdentity<Type extends Lengthwise>(arg: Type): Type {   console.log(arg.length);    return arg; } 

The first reason to avoid naming your type Type when using constraints is that it doesn't read very well within the method nor with multiple methods. Imagine the logging methods are much longer than 2 lines, and all of a sudden all you're reading is the word Type everywhere.

interface Lengthwise {   length: number; }  interface Widthwise {   width: number; }  function loggingLength<Type extends Lengthwise>(arg: Type): Type {   console.log(arg.length);   return arg; }  function loggingWidth<Type extends Widthwise >(arg: Type): Type {   console.log(arg.width);   return arg; }  

I think the following is much more readable:

interface Lengthwise {   length: number; }  interface Widthwise {   width: number; }  function loggingLength<TLengthwise extends Lengthwise>(arg: TLengthwise): TLengthwise {   console.log(arg.length);   return arg; }  function loggingWidth<TWidthwise extends Lengthwise>(arg: TWidthwise ): TWidthwise {   console.log(arg.length);   return arg; }  

Secondly it's very possible to have more then 1 generic and that makes the following invalid (hopefully for obvious reasons).

interface Lengthwise {   length: number; }  interface Widthwise {   width: number; }  function loggingLength<Type extends Lengthwise, Type extends Widthwise> (arg1: Type, arg2: Type) {   console.log(arg1.length);   console.log(arg2.width); }  

While the documentation uses Type for showing off simplistic and mundane examples, I highly suggest not using Type in any real world code.

like image 126
Erik Philips Avatar answered Dec 05 '22 15:12

Erik Philips


The example you provide is a function with a generic parameter. T (which does not have to be T. You could call it G.) is called a generic template where actual type of the T is replaced at runtime.

Imagine EntityAdapter has following implementation:

interface EntityAdapter<T> {    save(entity: T); } 

Below code returns an EntityAdapter which content is any. any could be number,string, object or anything.

let adapter1 = createEntityAdapter<any>(<parameters here>) 

Below code returns an EntityAdapter which content is Car.

let adapter2 = createEntityAdapter<Car>(<parameters here>) 

Basically Car is more specific than any so that you can get extra type safety. So how does this help?

In a nutshell, generic template can give you extra type safety. For example,

adapter1.save('I am string') // this works because `T` is `any` adapter1.save(new Car()) // this also works because `T` is `any`  adapter2.save('I am string') // this wont works  because `T` is `Car`, typescript compiler will complains adapter2.save(new Car()) //this works because `T` is `Car` 

Hope this helps.

like image 34
Emily Parker Avatar answered Dec 05 '22 16:12

Emily Parker