You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: packages/documentation/copy/en/handbook-v2/Type Manipulation/Generics.md
+123Lines changed: 123 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -426,3 +426,126 @@ A generic parameter default follows the following rules:
426
426
- If a default type is specified and inference cannot choose a candidate, the default type is inferred.
427
427
- A class or interface declaration that merges with an existing class or interface declaration may introduce a default for an existing type parameter.
428
428
- A class or interface declaration that merges with an existing class or interface declaration may introduce a new type parameter as long as it specifies a default.
429
+
430
+
## Variance Annotations
431
+
432
+
> This is an advanced feature for solving a very specific problem, and should only be used in situations where you've identified a reason to use it
433
+
434
+
[Covariance and contravariance](https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)) are type theory terms that describe what the relationship between two generic types is.
435
+
Here's a brief primer on the concept.
436
+
437
+
For example, if you have an interface representing an object that can `make` a certain type:
438
+
```ts
439
+
interfaceProducer<T> {
440
+
make():T;
441
+
}
442
+
```
443
+
We can use a `Producer<Cat>` where a `Producer<Animal>` is expected, because a `Cat` is an `Animal`.
444
+
This relationship is called *covariance*: the relationship from `Producer<T>` to `Producer<U>` is the same as the relationship from `T` to `U`.
445
+
446
+
Conversely, if you have an interface that can `consume` a certain type:
447
+
```ts
448
+
interfaceConsumer<T> {
449
+
consume: (arg:T) =>void;
450
+
}
451
+
```
452
+
Then we can use a `Consumer<Animal>` where a `Consumer<Cat>` is expected, because any function that is capable of accepting a `Cat` must also be capable of accepting an `Animal`.
453
+
This relationship is called *contravariance*: the relationship from `Consumer<T>` to `Consumer<U>` is the same as the relationship from `U` to `T`.
454
+
Note the reveral of direction as compared to covariance! This is why contravariance "cancels itself out" but covariance doesn't.
455
+
456
+
In a structural type system like TypeScript's, covariance and contravariance are naturally emergent behaviors that follow from the definition of types.
457
+
Even in the absence of generics, we would see covariant (and contravariant) relationships:
458
+
```ts
459
+
interfaceAnimalProducer {
460
+
make():Animal;
461
+
}
462
+
463
+
// A CatProducer can be used anywhere an
464
+
// Animal producer is expected
465
+
interfaceCatProducer {
466
+
make():Cat;
467
+
}
468
+
```
469
+
470
+
TypeScript has a structural type system, so when comparing two types, e.g. to see if a `Producer<Cat>` can be used where a `Producer<Animal>` is expected, the usual algorithm would be structurally expand both of those definitions, and compare their structures.
471
+
However, variance allows for an extremely useful optimization: if `Producer<T>` is covariant on `T`, then we can simply check `Cat` and `Animal` instead, as we know they'll have the same relationship as `Producer<Cat>` and `Producer<Animal>`.
472
+
473
+
Note that this logic can only be used when we're examining two instantiations of the same type.
474
+
If we have a `Producer<T>` and a `FastProducer<U>`, there's no guarantee that `T` and `U` necessarily refer to the same positions in these types, so this check will always be performed structurally.
475
+
476
+
Because variance is a naturally emergent property of structural types, TypeScript automatically *infers* the variance of every generic type.
477
+
**In extremely rare cases** involving certain kinds of circular types, this measurement can be inaccurate.
478
+
If this happens, you can add a variance annotation to a type parameter to force a particular variance:
479
+
```ts
480
+
// Contravariant annotation
481
+
interfaceConsumer<inT> {
482
+
consume: (arg:T) =>void;
483
+
}
484
+
485
+
// Covariant annotation
486
+
interfaceProducer<outT> {
487
+
make():T;
488
+
}
489
+
490
+
// Invariant annotation
491
+
interfaceProducerConsumer<inoutT> {
492
+
consume: (arg:T) =>void;
493
+
make():T;
494
+
}
495
+
```
496
+
Only do this if you are writing the same variance that *should* occur structurally.
497
+
498
+
> Never write a variance annotation that doesn't match the structural variance!
499
+
500
+
It's critical to reinforce that variance annotations are only in effect during an instantiation-based comparison.
501
+
They have no effect during a structural comparison.
502
+
For example, you can't use variance annotations to "force" a type to be actually invariant:
503
+
```ts
504
+
// DON'T DO THIS - variance annotation
505
+
// does not match structural behavior
506
+
interfaceProducer<inoutT> {
507
+
make():T;
508
+
}
509
+
510
+
// Not a type error -- this is a structural
511
+
// comparison, so variance annotations are
512
+
// not in effect
513
+
const p:Producer<string|number> = {
514
+
make():number {
515
+
return42;
516
+
}
517
+
}
518
+
```
519
+
Here, the object literal's `make` function returns `number`, which we might expect to cause an error because `number` isn't `string | number`.
520
+
However, this isn't an instantiation-based comparison, because the object literal is an anonymous type, not a `Producer<string | number>`.
521
+
522
+
> Variance annotations don't change structural behavior and are only consulted in specific situations
523
+
524
+
It's very important to only write variance annotations if you absolutely know why you're doing it, what their limitations are, and when they aren't in effect.
525
+
Whether TypeScript uses an instantiation-based comparison or structural comparison is not a specified behavior and may change from version to version for correctness or performance reasons, so you should only ever write variance annotations when they match the structural behavior of a type.
526
+
Don't use variance annotations to try to "force" a particular variance; this will cause unpredictable behavior in your code.
527
+
528
+
> Do NOT write variance annotations unless they match the structural behavior of a type
529
+
530
+
Rmember, TypeScript can automatically infer variance from your generic types.
531
+
It's almost never necessary to write a variance annotation, and you should only do so when you've identified a specific need.
532
+
Variance annotations *do not* change the structural behavior of a type, and depending on the situation, you might see a structural comparison made when you expected an instantiation-based comparison.
533
+
Variance annotations can't be used to modify how types behave in these structural contexts, and shouldn't be written unless the annotation is the same as the structural definition.
534
+
Because this is difficult to get right, and TypeScript can correctly infer variance in the vast majority of cases, you should not find yourself writing variance annotations in normal code.
535
+
536
+
> Don't try to use variance annotations to change typechecking behavior; this is not what they are for
537
+
538
+
You *may* find temporary variance annotations useful in a "type debugging" situation, because variance annotations are checked.
539
+
TypeScript will issue an error if the annotated variance is identifiably wrong:
540
+
```ts
541
+
// Error, this interface is definitely contravariant on T
542
+
interfaceFoo<outT> {
543
+
consume: (arg:T) =>void;
544
+
}
545
+
```
546
+
However, variance annotations are allowed to be stricter (e.g. `in out` is valid if the actual variance is covariant).
547
+
Be sure to remove your variance annotations once you're done debugging.
548
+
549
+
Lastly, if you're trying to maximize your typechecking performance, *and* have run a profiler, *and* have identified a specific type that's slow, *and* have identified variance inference specifically is slow, *and* have carefully validated the variance annotation you want to write, you *may* see a small performance benefit in extraordinarily complex types by adding variance annotations.
550
+
551
+
> Don't try to use variance annotations to change typechecking behavior; this is not what they are for
0 commit comments