Boolean
Boolean type declaration:
let isDone: boolean = false;
Copy the code
Number
As in JavaScript, all numbers in TypeScript are either floating point values or BigIntegers. These floating-point numbers are of type number and bigint is of type Bigint. In addition to hexadecimal and decimal, TypeScript also supports binary and octal, introduced in ECMAScript 2015.
let decimal: number = 6;
let hex: number = 0xf00d;
let binary: number = 0b1010;
let octal: number = 0o744;
let big: bigint = 100n;
Copy the code
String
In typescript, we use string to define a variable of string type. As in javascript, variables can be enclosed in single or double quotes:
let color: string = "blue";
color = 'red';
Copy the code
You can also use template strings, which can span multiple lines and have nested expressions. String values are enclosed in ‘ ‘backquotes, and expressions are ${expr}.
let fullName: string = `Bob Bobbington`;
let age: number = 37;
let sentence: string =
"Hello, my name is " +
fullName +
".\n\n" +
"I'll be " +
(age + 1) +
" years old next month.";
Copy the code
Array
There are two ways to declare an array variable,
Method 1: Element type + []
let list: number[] = [1.2.3];
Copy the code
Method 2: Use the generic type Array
let list: Array<number> = [1.2.3];
Copy the code
Tuple (Tuple)
Tuples are used to define arrays with a fixed number of elements that can have different types:
// Declare a tuple type
let x: [string.number];
// Initialize it
x = ["hello".10]; // OK
// Initialize it incorrectly
x = [10."hello"]; // Error
// Type 'number' is not assignable to type 'string'.
// Type 'string' is not assignable to type 'number'.
Copy the code
When accessing an element with a known index, the type of the value obtained is that of the corresponding location:
// OK
console.log(x[0].substring(1));
console.log(x[1].substring(1));
// Property 'substring' does not exist on type 'number'.
Copy the code
An error will be reported when accessing a value in a tuple from a subscript outside the tuple index range:
x[3] = "world";
// Tuple type '[string, number]' of length '2' has no element at index '3'.
console.log(x[5].toString());
// Object is possibly 'undefined'.
// Tuple type '[string, number]' of length '2' has no element at index '5'.
Copy the code
Enum (enumeration)
Enumerations are one of the few TypeScript features that isn’t a JavaScript type extension. Typescript provides numeric and string-based enumerations. Enumerations are defined using the Enum keyword.
Numeric enums
enum Direction {
Up = 1,
Down,
Left,
Right,
}
Copy the code
·Down =2, Left=3, Right = 4, if there is no initial value:
enum Direction {
Up,
Down,
Left,
Right,
}
Copy the code
In this case, the initial value of UP is 0, and the values of subsequent elements are automatically increased based on this value. Auto-increment is useful if we don’t care about the values of members, as long as their values are different.
Or you can explicitly assign a value to each element,
enum Color {
Red = 1,
Green = 2,
Blue = 4,}Copy the code
Another handy feature is that you can also get the name of the corresponding value from the value (more on reverse mapping later)
enum Color {
Red = 1,
Green,
Blue,
}
let colorName: string = Color[2];
// Displays 'Green'
console.log(colorName);
Copy the code
Using an enumeration is simple: Access the properties of the enumeration type and declare the type using the name of the enumeration:
enum UserResponse {
No = 0,
Yes = 1,}function respond(recipient: string, message: UserResponse) :void {
// ...
}
respond("Princess Caroline", UserResponse.Yes);
Copy the code
An enumeration without an initializer must either go first or after a numeric enumeration initialized with a numeric constant or other constant enumeration member. In other words, the following situations are not allowed:
enum E {
A = getSomeValue(),
B,
// Enum member must have initializer.
}
Copy the code
String enums(String enumeration)
The concept of string enumeration is similar to numeric enumeration, but there are some runtime differences. A member of a string enumeration type must be constants initialized using either a string or another string enumeration member.
enum Direction {
Up = "UP",
Down = "DOWN",
Left = "LEFT",
Right = "RIGHT",}Copy the code
Although string enumeration types do not have the behavior of auto-growth, the benefit of string enumeration is that they serialize well. In other words, when debugging numeric enumeration runtime values, numeric enumeration property values do not explicitly tell us what they represent (although there is a reverse mapping to get the property names corresponding to property values), but string enumeration can give us a meaningful and more readable value. This eliminates the need to rely on the name of the enumeration member.
Heterogeneous Enums
Typescript supports mixed string enumerations and numeric enumerations.
enum BooleanLikeHeterogeneousEnum {
No = 0,
Yes = "YES",}Copy the code
But this usage is generally not recommended.
Compute members and constant members
Each enumeration member has a value associated with it, either a constant or a computed value. An enumeration member is considered constant if:
- If it is the first member of an enumeration and there is no initializer, in this case it is initialized to 0.
// E.X is constant: enum E { X, } Copy the code
- There is no initializer and the enumerator preceding it is a numeric constant. In this case, the value of the current enumerator is the value of the previous enumerator plus 1.
// All enum members in 'E1' and 'E2' are constant.
enum E1 {
X,
Y,
Z,
}
enum E2 {
A = 1,
B,
C,
}
Copy the code
- Enumeration members are initialized using a constant enumeration expression. Constant enumeration expressions are
TypeScript
A subset of expressions that can be fully evaluated at compile time. An expression is a constant enumeration expression if:
- Literal enumeration expressions (mainly string or numeric literals)
- A reference to a previously defined constant enumeration member (which can be derived from a different enumeration)
- A constant enumeration expression with parentheses
- For constant enumeration expressions
+, -, ~
One of the unary operators The +, -, *, /, %, < <, > >, > > >, &, |, ^
Binary operator with constant enumeration expressions as operands
Evaluating a constant enumeration expression to NaN or Infinity will result in a compile-time error.
Enumeration members in all other cases are treated as values that need to be computed.
enum FileAccess {
// constant members
None,
Read = 1 << 1,
Write = 1 << 2,
ReadWrite = Read | Write,
// computed member
G = "123".length,
}
Copy the code
Union enums and enum member types
There is also a special subset of constant enumerators: literal enumerators. Literal enumerators are constant enumerators whose values have no initial value or whose initial value is initialized to:
- Any string literal (e.g.,” foo”,”bar”,”baz”)
- Any number literal (e.g. 1,100)
- Or anything with a unary operator
-
Number literal (e.g. -1, -100)
Some special semantics arise when all members in an enumeration have literal enumeration values.
First, the enumeration members themselves become types. For example, we can define that some members of an object can only have the values of the enumeration members.
enum ShapeKind {
Circle,
Square,
}
interface Circle {
kind: ShapeKind.Circle;
radius: number;
}
interface Square {
kind: ShapeKind.Square;
sideLength: number;
}
let c: Circle = {
kind: ShapeKind.Square,
// Type 'ShapeKind.Square' is not assignable to type 'ShapeKind.Circle'.
radius: 100};Copy the code
Second, the enumeration type itself actually becomes a union of each enumeration member. Using federated enumerations allows the type system to know the exact values that exist in the enumerations. Typescript catches bug comparisons. For example:
enum E {
Foo,
Bar,
}
function f(x: E) {
if(x ! == E.Foo || x ! == E.Bar) {// This condition will always return 'true' since the types 'E.Foo' and 'E.Bar' have no overlap.}}Copy the code
In this example, we first compare whether x is not equal to E. fuse. If this is true, the judgment is over. But if that’s not true then x is e. So it’s pointless to compare E.Bar later.
Runtime enumeration
Enumerations are real objects at runtime, such as:
enum E {
X,
Y,
Z,
}
function f(obj: { X: number }) {
return obj.X;
}
// Works, since 'E' has a property named 'X' which is a number.
f(E);
Copy the code
You can use enumerations as objects directly here.
Compile-time enumeration
Use the keyof Typeof keyword to get a type that represents all enumerated keys as strings.
enum LogLevel {
ERROR,
WARN,
INFO,
DEBUG,
}
/** * This is equivalent to: * type LogLevelStrings = 'ERROR' | 'WARN' | 'INFO' | 'DEBUG'; * /
type LogLevelStrings = keyof typeof LogLevel;
function printImportant(key: LogLevelStrings, message: string) {
const num = LogLevel[key];
if (num <= LogLevel.WARN) {
console.log("Log level key is:", key);
console.log("Log level value is:", num);
console.log("Log level message is:", message);
}
}
printImportant("ERROR"."This is a message");
Copy the code
Reverse mappings
Members of a numeric enumeration type have a reverse mapping from value to name, as mentioned earlier.
enum Enum {
A,
}
let a = Enum.A;
let nameOfA = Enum[a]; // "A"
Copy the code
Typescript compiles this code to the following JS code:
"use strict";
var Enum;
(function (Enum) {
Enum[Enum["A"] = 0] = "A";
})(Enum || (Enum = {}));
let a = Enum.A;
let nameOfA = Enum[a]; // "A"
Copy the code
The end result is to compile the enumeration into an object that stores both a mapping for name->value and a mapping for value->name.
It is important to note that string enumerations do not have this reverse mapping capability.
Const enums
Constant enumerations are defined with the const modifier and can only use constant enumeration expressions. Unlike ordinary enumerations, which do not have reverse mapping access, constant enumerations are completely removed at compile time, leaving only the values of the enumerators where they are used. This is possible at compile time because the enumerators are not evaluated. Here’s an example:
enum Direction {
Up,
Down,
Left,
Right,
}
let directions = [
Direction.Up,
Direction.Down,
Direction.Left,
Direction.Right,
];
Copy the code
This example only exists when the access to the enumeration member is not reversed (such as Direction[0]). If the js code compiled without constant enumeration is:
var Direction;
(function (Direction) {
Direction[Direction["Up"] = 0] = "Up";
Direction[Direction["Down"] = 1] = "Down";
Direction[Direction["Left"] = 2] = "Left";
Direction[Direction["Right"] = 3] = "Right";
})(Direction || (Direction = {}));
var directions = [
Direction.Up,
Direction.Down,
Direction.Left,
Direction.Right,
];
Copy the code
Use const to define a constant enumeration type:
const enum Direction {
Up,
Down,
Left,
Right,
}
...
Copy the code
Compiled JS code:
var directions = [
0 /* Up */.1 /* Down */.2 /* Left */.3 /* Right */,];Copy the code
The benefits of this are obvious. Without the extra code for reverse mapping, the final result is less code.
External enumeration (Ambient Enums)
External enumerations are used to describe existing enumerations, which say:
// Note: Assume no other file has actually created a Foo var at runtime
declare enum Foo { Bar }
var s = 'Bar';
var b = Foo[s]; // Fails
Copy the code
Assuming that the Foo enumeration is not defined anywhere else, this code will not report an error when compiling, but it will report an error when running because the Foo enumeration is not defined.
One important difference between external and non-external enumerations is that in a non-external enumeration, a member without an initializer is considered a constant if the previous enumerator is a constant. In contrast, external (and nonconst) enumerators without initializers are always treated as computed enumerators.
Unknown
When defining an unknown type of a variable, you can define the type of the variable as unknown. Unknown tells the compiler that the current variable can be of any type, so we don’t know the type of the variable so we give it an unknown type.
let notSure: unknown = 4;
notSure = "maybe a string instead";
// OK, definitely a boolean
notSure = false;
Copy the code
A variable of unknown type can be narrowed down to a more specific scope by typeof, comparison checking, or more advanced type protection:
declare const maybe: unknown;
// 'maybe' could be a string, object, boolean, undefined, or other types
const aNumber: number = maybe;
// Type 'unknown' is not assignable to type 'number'.
if (maybe === true) {
// TypeScript knows that maybe is a boolean now
const aBoolean: boolean = maybe;
// So, it cannot be a string
const aString: string = maybe;
// Type 'boolean' is not assignable to type 'string'.
}
if (typeof maybe === "string") {
// TypeScript knows that maybe is a string
const aString: string = maybe;
// So, it cannot be a boolean
const aBoolean: boolean = maybe;
// Type 'string' is not assignable to type 'boolean'.
}
Copy the code
Any
Use the any type when you want to bypass type checking
declare function getValue(key: string) :any;
// OK, return value of 'getValue' is not checked
const str: string = getValue("myString");
Copy the code
The difference between unknown and unknown is that when a variable is of type any, it can access any property of the variable. Ts does not check whether the property exists or what type the property is.
let looselyTyped: any = 4;
// OK, ifItExists might exist at runtime
looselyTyped.ifItExists();
// OK, toFixed exists (but the compiler doesn't check)
looselyTyped.toFixed();
let strictlyTyped: unknown = 4;
strictlyTyped.toFixed();
// Object is of type 'unknown'.
let looselyTyped: any = {};
let d = looselyTyped.a.b.c.d;
// let d: any
Copy the code
These benefits of using any come at the expense of type safety, which defeats the purpose of using Typescript. Avoid using any if you can.
Void
The void type is kind of like the opposite of any, meaning there is no type. This is usually used when defining a function that does not return a value:
function warnUser() :void {
console.log("This is my warning message");
}
Copy the code
Declaring a variable as void is not useful, since it can only be null (without specifying strictNullChecks) or undefined:
let unusable: void = undefined;
// OK if `--strictNullChecks` is not given
unusable = null;
Copy the code
Null, and Undefined
In Typescript, the values null and undefined have their own types, null and undefined, respectively. If the current variable is declared null or undefined, we can only assign null and undefined, respectively:
// Not much else we can assign to these variables!
let u: undefined = undefined;
let n: null = null;
Copy the code
The types NULL and undefined are subtypes of other types. For example, null or undefined can be assigned to a variable of type number.
But using –strictNullChecks, null and undefined can only be assigned to variables of type unknown, any and null and undefined (undefined can also be assigned to variables of type void).
Never
Never indicates the type of a value that will never occur. For example, a function that never returns or always throws errors can be defined as having a return value of type never. Never is also obtained when a variable is bound by a guard of a type that is never true.
The type never is a subtype of any type and can be assigned to any type. But only never can be assigned to never, and any cannot be assigned to never.
// Function returning never must not have a reachable end point
function error(message: string) :never {
throw new Error(message);
}
// Inferred return type is never
function fail() {
return error("Something failed");
}
// Function returning never must not have a reachable end point
function infiniteLoop() :never {
while (true) {}}Copy the code
never
Use scenarios:
interface Foo {
type: 'foo'
}
interface Bar {
type: 'bar'
}
type All = Foo | Bar
// Discriminated union (TS) is a discriminated union (discriminated union) :
function handleValue(val: All) {
switch (val.type) {
case 'foo':
// Here val is narrowed to Foo
break
case 'bar':
// Val is Bar in this case
break
default:
// In this case, val is never
const exhaustiveCheck: never = val
break}}Copy the code
If a new type is added that is not handled in the switch:
type All = Foo | Bar | Baz
Copy the code
In the end, the val in default is narrowed down to Baz, because there is no type to assign to never (except for never itself), making it impossible to compile. In this way you ensure that the handleValue always exhauzes All possible types of All.
Object
Object represents a non-primitive type, that is, a type other than number, string, Boolean, symbol, null, or undefined.
An API like object.create is better represented by using the object type. Such as:
declare function create(o: object | null) :void;
// OK
create({ prop: 0 });
create(null);
create(undefined); // with `--strictNullChecks` flag enabled, undefined is not a subtype of null
// Argument of type 'undefined' is not assignable to parameter of type 'object | null'.
create(42);
// Argument of type '42' is not assignable to parameter of type 'object | null'.
create("string");
// Argument of type '"string"' is not assignable to parameter of type 'object | null'.
create(false);
// Argument of type 'false' is not assignable to parameter of type 'object | null'.
Copy the code
## Assertions
Type assertions are sometimes used when we explicitly know the type of a value and want to tell the compiler to treat the value as a certain type. Type assertions are used only for compile-time type checking and do not perform any run-time conversions on the value.
There are two ways to make type assertions:
Use as syntax
let someValue: unknown = "this is a string";
let strLength: number = (someValue as string).length;
Copy the code
Use Angle bracket syntax
let someValue: unknown = "this is a string";
let strLength: number = (<string>someValue).length;
Copy the code
The two approaches are equivalent, which one you prefer is a matter of personal preference, but note that only as syntax can be used in JSX
# # about Number, String, Boolean, Symbol, and the Object
Need to distinguish between Number, String, Boolean, Symbol, the Object and the small Number, String, Boolean, Symbol, Object, only lowercase can serve as a type.
The resources
- Enums
- Basic Types
- What exactly does the never type in TypeScript do?
- How do the different enum variants work in TypeScript?