Overview
TypeScript has had string literal types for a while. With TypeScript 2.0, the type system was extended by several new literal types:
- Boolean literal types
- Numeric literal types
- Enum literal types
TypeScript 2.1 improves the type inference for all of these types when a const
variable or readonly
property has a literal initializer.
Better Inference for const
Variables
Let’s start with local variables and the var
keyword. When TypeScript sees the following variable declaration, it infers the type string
for the baseUrl
variable:
|
|
The same goes for { 适用于 } variables declared with the let
keyword:
|
|
Both variables are inferred to have type string
because they can change at any time. They are initialized with a literal string value, but they can be modified later.
However, if a variable is declared using the const
keyword and initialized with a string literal, the inferred type is no longer string
, but the corresponding string literal type:
|
|
The inferred type should be as specific as possible since the value of a constant string variable can never change. It’s impossible for the baseUrl
variable to hold any other value than "https://example.com/"
. This information is now reflected in the type system.
Literal type inference works for other primitive types, too. If a constant is initialized with an immediate { An immediate result, action, or reaction happens or is done without any delay } numeric or boolean value, a literal type is inferred as well:
|
|
Similarly, a literal type is inferred when the initializer is an enum value:
|
|
Note that direction
is typed as FlexDirection.Column
, which is an enum literal type. Had we used the let
or var
keyword to declare the direction
variable, its inferred type would’ve been FlexDirection
instead.
Better Inference for readonly
Properties
Similar to local const
variables, readonly properties with a literal initializer are inferred to be of a literal type as well:
|
|
Read-only class properties can only be initialized right away or from within a constructor. Attempting to change the value in other places results in a compile-time error. Therefore, it is reasonable { 合理的 } to infer a literal type for a read-only class property because its value doesn’t change (given that the TypeScript program is type-correct).
Of course, TypeScript can’t know what happens at run-time: properties marked with readonly
can be changed at any time by some piece of JavaScript code. The readonly
modifier is meant to restrict access to a property from within TypeScript code, but it has no run-time manifestation { 表现,显现 } at all. That is, it is compiled away and doesn’t show up in the generated JavaScript code.
Usefulness of Inferred Literal Types
You might ask yourself why it is useful to infer literal types for const
variables and readonly
properties. Consider the following code example:
|
|
If the HTTP_GET
constant was inferred to have type string
instead of "GET"
, you’d get a compile-time error because you wouldn’t be able to pass HTTP_GET
as the second argument to the request
function:
|
|
Of course, it’s not allowed to pass any arbitrary string as a function argument if the corresponding parameter only allows two specific string values. When the literal types "GET"
and "POST"
are inferred for the two constants, though, it all works out { If a situation works out well or works out, it happens or progresses in a satisfactory way }.
Next up: widening and non-widening literal types and the difference between the two.