Table of contents
- What exactly is type widening?
- Why is it important to understand type widening?
- How does structural typing (subsets to superset types) let typescript widening errors occur?
- Is there any way to predict the outcome of type widening?
- Give an example of the rule: a variable's type shouldn't change after it's declared
- Give another rule governing how TS treats properties of objects.
- What is a significant limitation imposed on objects as a result of this rule
- List 4 methods to control the TS widening process.
- Which of these apply to single value variables and which to objects and tuples? Give examples
What exactly is type widening?
Type widening is the process by which the type checker assigns a type to a variable initialized with a constant value when no type annotations are provided.
Example: Given
let x = 'v'
When type widening is done x will have an inferred type of 'string'.
Why is it important to understand type widening?
So that we understand errors that are thrown due to unassignable values caused as a result of type widening.
Example: Given
let x = 'banana'
let fruit: 'orange'|'banana' = orange
fruit = x;
> error: cannot assign string to type of 'orange'|'banana'
Without an awareness of type widening, we would struggle to understand why a string looking variable, fruit, cannot be assigned the value of another seemingly string looking variable, x. With an awareness of type widening, we can understand that type of fruit is a much more specific type than that of string and following structural typing cannot permit an assignation of type string.
If you're struggling with structural typing remember it as the process that allows subsets to be assigned to supersets, and be careful in how you determine which the subset is and which the superset is. For instance, subsets can 'look' bigger than supersets, but typically a subset has more properties defined than a superset, or put another way, {} is a very large set while {x: string} is also a very large set but smaller than {}.
How does structural typing (subsets to superset types) let typescript widening errors occur?
It only allows narrower to wider type assignations, but not wider to narrower assignations, so in the case where a constant's type in inferred as, say, string, assigning that type to a narrower type of 'banana' | 'apple' causes an error with respect to structural typing.
Is there any way to predict the outcome of type widening?
Yes. Although very much an ambiguous process, we can predict the outcome of a widened type depending on the following:
1. If we used let or const
Let leads to wider inferences than const which typically only allows for the very specific type of that value.
Example:
let x = 'x' > type string
const x = 'x' > type 'x'
2. If we used the as const annotation
Different from the const keyword which exists in value space to declare constant variables, as const chooses the most specific type when used, and is most useful in controlling the types of object properties.
Example:
const obj = {
a: 'x' as const
b: 'sugar'
}
Without as const, property 'a' would have been inferred as 'string.' With as const, it is inferred as having type 'x'.
Similarly,
const obj = {
a: 'x',
b: 'sugar'
} as const
Without as const, obj would have been inferred as obj: {a: string, b: string}
. With const it is inferred as obj: {readonly a: 'x', readonly b: 'sugar'}
.
3. If we provided a type declaration annotation
Providing a type annotation narrows the type inference to the type provided.
Example
let x: 'orange' = banana > throws an error, string cannot be assigned to type 'orange'
4. If it is an element of an object
Elements of objects are treated as though having been declared using the let keyword.
5. If we declared the object all at once or in bits
Type widening only allows for inference on elements present in an object at declaration time. It does not allow for new object properties.
Give an example of the rule: a variable's type shouldn't change after it's declared
let x = 'orange'
x = 1
Typescript will constrain a constants type to limit it to the set of values that can be assigned to that variable without needing to change the type of the variable. So an assignation like the one above where we're trying to assign an entirely different type of 'number' to a type of 'string' will throw an error.
Give another rule governing how TS treats properties of objects.
Elements of objects are treated as though having been declared by let
What is a significant limitation imposed on objects as a result of this rule
You cannot add new properties which were not originally part of the object when it was declared.
List 4 methods to control the TS widening process.
- Use const
- Use as const
- Use a type declaration annotation
- Declare objects in full
Which of these apply to single value variables and which to objects and tuples? Give examples
They all apply to both, except perhaps the last method. The as const method applies to objects and tuples in particular.
Using as const on an array like object is great for creating a tuple type.
Example:
let tuple = [1,2] as const
> type is inferred as readonly [1 , 2]
compared to
let tuple = [1,2]
> type is inferred as number[]