Typescript: A static Type checker based on the Structural Type System, as opposed to javascript, that catches errors and fixes them before you run code.
Two key points emerge from typscript’s definition:
- Structure type: An object describes its structure in terms of type, with emphasis on
type
andstructure
For example, what type is each element in an array - Desired goal: Catch bugs before the code runs and help you find them as early as possible
function print(name: string, age: number) {
console.log(name,age)
}
Copy the code
The first parameter name of the function print must be a string, and the second parameter age must be a number. These two groups together describe the parameter type structure of the function.
Type and structure are the basis of typescript’s static type checking.
Basic Type Basic Type
Typescript primitive types are basic data types that are the basic units of composition into complex types. Just like learning words, he is a stroke.
1. Boolean value types
// Boolean Specifies a Boolean value
let boo: boolean = true
Copy the code
2. The value is a string
// string The character string
let str: string = 'hello world'
Copy the code
3. Number type
/ / number number
let num: number = 0
Copy the code
4. Null and undefined
// undefined means undefined, declare a variable, attribute is undefined, actually not useful
let unde: undefined = undefined;
// undefined is used to indicate a variable, attribute, or parameter
let num: undefined | number = 0;
// In the same way, declaring a variable to be null is not really useful, more for union types
let maybe: null | string = null;
// They are also subtypes of all other types
let str: string;
let a: undefined = undefined;
let b: null = null;
// The type check passes because undefined, null is a subtype of another type
str = a;
str = b;
// We will not define a single variable as undefined,null
// is more of an optional, associative type, so what is the design for typescript? It's a little weird
str.toString(); // Uncaught TypeError: Cannot read property 'toString'
StrictNullChecks, null and undefined can only be assigned to unknownn, any
// This is a good way to circumvent the above problem
let c: unknown = null;
Copy the code
5. Never type
// It means never happen, impossible to get to
// It is a subtype of any type
// Because it is non-occurring and unreachable, it cannot be subtyped by any other type, nor can it be assigned to any other type, except to itself
// the value assigned to never is meaningless
// Return a never error. This cannot happen
function fail() {
return error("Something failed");
}
// It is obviously an endless loop, never to be reached
function infiniteLoop() :never {
while (true) {}}let nev1: never;
let nev2: never;
let anyType: any;
let str: string;
let num:number
// Because it cannot be a subtype of any type, it can only be assigned to itself
str = nev1
num = nev1
// None of the types can be subtypes of never because they have no practical meaning
nev1 = anyType // error
// never can be assigned to itself.
nev1 = nev2
Copy the code
6. Any type
// any a derivation without constraints, any derivation is true
const word: any = 'any type will go'
// Declare word to be of type any. Typescript assumes that the type derivation is true when it does type checking
// Instead of doing type checking, you can write it any way you want
Uncaught TypeError: word. Action is not a function
word.action()
Copy the code
7. The empty type void
// void is an empty value used to indicate that a function has no return value
// If the function does not return, it actually returns undefined,
// undefined is a subtype of void
function test() :void {
// Return does not have any value
}
// Declare a variable to be a void value
let unusable: void = undefined;
// Only if '--strictNullChecks' is closed can null be assigned to void
unusable = null;
Copy the code
8. Unknown type
// Unknown for the moment, but give me more information, you can deduce it
// Like a detective, gradually narrow the scope and define the type
let maybe: unknown;
// Narrow range is a number number type
if (typeof maybe === "number") {
let num: number = maybe;
// Maybe is a number and cannot be assigned to a string
let str:string = maybe; // error
}
Copy the code
9. Array types
// There are two ways to express it
// This method is recommended to be more concise
let list: number[] = [1.2.3]
2 / / way
let arr: Array<string> = ['a'.'b'.'c']
Copy the code
10. Tuple type
/ / the tuple tuples
let pairs: [ string.number ] = []
Copy the code
11. Enumeration types
/ / enum enumeration
enum Status{
Draft = 1 // Start from 0 by default
Audit = 3
}
let status:Status = Status.Draft
Copy the code
12. Object
/ / Object
// anything that is not number, string, boolean, bigint, symbol, null, or undefined
// To put it another way: basic data types in JS
// It is recommended not to use it during development as it does not provide any useful type information
const obj: object = null;
// If the type derivation is true, the actual operation will fail
obj.toString();
Copy the code
Enum enumeration
Javascript does not provide an enumeration; it is one of typescript’s provided data types. From the values of enumerators, there are three classes:
- Enumeration: The value of each enumerator is a number
- String enumeration: The value of each enumeration member is a string
- Mixed enumeration: The value of each enumerator, which can be a number or a string, is technically supported and not recommended
// Enumeration of numbers
// Only the first one is initialized. The following ones are incremented by default
// So Error is 2, Warning is 3, and so on...
enum Status {
Success = 1.Error./ / 2
Warning, / / 3
Info, / / 4
}
// String enumeration
enum Direction {
Up = "UP",
Down = "DOWN",
Left = "LEFT",
Right = "RIGHT",}// String enumeration
// Technical support, against single responsibility, not recommended
enum MixDirection {
Up = 1,
Down = "DOWN",}Copy the code
assertions
Why use assertions? Sometimes typescript inferences based on the type information it currently knows may not be as accurate as the developer.
const el = document.getElementById("id");
// Type error: attribute autocomplete may not be on 'HTMLElement'
// It is obvious that the developer knows what type el is, such as a textarea, and wants to set the text to be filled automatically using the browser's memory function
el.autocomplete = 'on';
// Use assertions to tell typescript el is an HTMLTextAreaElement type
const el = document.getElementById("id") as HTMLTextAreaElement;
el.autocomplete = "on";
// Assertions can be expressed in two ways: the keyword as and Angle brackets
// as keyword
// Note that in JSX, only as is allowed
let someValue: unknown = "this is a string";
let strLength: number = (someValue as string).length;
// Angle brackets
let otherValue: unknown = "this is a string";
let strLen: number = (<string>otherValue).length;
Copy the code
Inteface interface
Interface: a core typesctipt concept, type checking focuses on the shape of a value, and compatibility is based on structure subtypes. Interface can theoretically represent any type, such as an object, a fuction function, or a class
How can an interface describe the type of an object?
Describe the properties of an object in terms of type, right? There are several aspects
- What are the properties? In terms of quantity
- A specific attribute: optional or mandatory
- Attribute assignment operations: readable or writable
interface ObjectShape {
// Mandatory property: a colon + type declaration
name: string;
// a question mark '? ': indicates an optional attribute, indicating that the attribute may not existsex? :number;
// Readonly modifier: indicates a read-only property and cannot be modified
readonly height: number;
// Optional read-only property: via readonly + '? ', two modifiers combined
readonlyhobby? :string;
// Index signature: index type
// There are many attributes, which cannot be listed one by one
[prop: string]: unknown;
}
Copy the code
How to describe the type of a function using an interface?
// funciton shape
interface FuncShape {
(str: string.num: number) :void;
}
// the first parameter STR has a different name than s, and the second parameter num has a different name than n
// typescript focuses on type, shape and structure
// As long as the shape is consistent, the named parameter name can be different
const func: FuncShape = function (s: string, n: number) {};
Copy the code
How to describe a class type using an interface?
A class has three main parts to describe its type:
- Constructor: The type of the argument
- Properties: visibility, scope control of access
- Method: Because a method is essentially a function, everything else is a declaration of a function type
// When describing the type of a class, keep in mind that there are two types of class: static and instance
// The static side is the class itself, and the instance side is the new operation to generate the instance
// There are two ways to describe the type of a class using interface
// Constructors are static methods and should be declared separately
interface ConstructorShape {
new (str: string): InstanceShape;
}
// The instance type
interface InstanceShape {
print(str: string) :void;
}
// Pass the constructor as an argument to another function
function createInstance(ctor: ConstructorShape, str: string) :InstanceShape {
return new ctor(str);
}
// We recommend that you use the second method, which is more intuitive and simpler
const InstacneClass: ConstructorShape = class InstacneClass
implements InstanceShape {
constructor(str: string) {}
print(str: string) {
// do something}};const instance: InstanceShape = new InstacneClass("hello world");
Copy the code
The above code only describes the types of constructors: arguments. The second and third points are on the instance side. You can look at the section on class types subclasses, derived classes, to get closer to actual development
Interfaces inherit from each other
interface Shape {
color: string;
}
interface PenStroke {
penWidth: number;
}
Square supports multiple inheritance via the extends keyword
// Inherit color: sting from Shape
Inherit the type declaration of penWidth: number from PenStroke
interface Square extends Shape, PenStroke {
sideLength: number;
}
Copy the code
Interface inheritance Class
The interface inherits the class member type declaration of a class. Note that private class members can only be obtained by inheriting the declared class.
// Unique is a private attribute of this class and can only be obtained by subclassing it
class Animal {
private unique: string;
}
interface Dog extends Animal {
run(): void;
}
// Tendy is not a subclass of Animal, so it does not have a unique private property
// But Dog inherits Animal's member declaration and must provide unique
// Tendy does not implement Dog interface
class Teddy implements Dog {
run() {
// do something}}// Husky is Animal and gets unique
class Husky extends Animal implements Dog {
run() {
// do something}}Copy the code
Class class type declaration?
A class type check is divided into two parts: the class itself, and the subclasses, derived classes, and instances generated by new
- Class itself is a static property/method declared using the keyword static
- Subclass, derived class, new generated instance: implemented with puublic, private, and protected modifiers
class Production {
// Static part:
constructor(readonly type: string) {} // Constructor static method, native
// Static attributes
static uuid = "__hash__";
// Static method
static action() {
// do something
}
// Subclass, derived class, instance generated by new
// Access is only allowed within the Production class
private secret: string; // #name: string; The new version specification defines private as such
// Allow access within Production classes, subclasses
protected serial: number;
// Access to an instance is not restricted
public price: number;
// the branch decorator group
public readonly level: number = 0;
public qualified(str: string = "") {
console.log(`${Production.uuid === this.secret + str}`);
}
// Calculate the accessors attribute
get fullName() {
return "hello kitty";
}
set fullName(str) {
// do something}}Copy the code
An abstract class
To abstract a series of classes, the same as defining each class to achieve, for example: to abstract a series of mobile phones, mobile phones have three core functions: 1. 2. Send a message
abstract class Phone {
// Define abstract methods to be implemented separately in subclasses
abstract call(): string;
abstract message(): string;
}
class Nokia implements Phone {
call() {
return "Nokia is calling the number";
}
message() {
return "Nokia"; }}class Motorola implements Phone {
call() {
return "Motorola is calling the number";
}
message() {
return "Motorola"; }}Copy the code
Use a class as an interface
// See interface inheriting Class above
class Point {
x: number;
y: number;
}
interface Point3d extends Point {
z: number;
}
let point3d: Point3d = { x: 1.y: 2.z: 3 };
Copy the code
Function Indicates the type of the Function
A function type declaration can be divided into two parts: the parameter type of the function and the return value type of the return
// The STR argument type is a string and must be supplied
// The num argument is an optional number
// The sex argument is an optional number. If it is not provided, the default value 0 is used
// The restParams parameter is a residual parameter, allowing any type
The return value of the function is a string
function print(
str: string, num? :number,
sex = 0. restParams: unknown[]) :string {
// do something
return "hello world";
}
Copy the code
This execution context
Before the arrow function, each new function defined the function’s this value in terms of how it was called:
- If the function is a constructor, the this pointer points to a new object
- In strict mode function calls, this refers to undefined
- If the function is a method of an object, its this pointer points to that object
- , etc.
Context of the arrow function:
- Arrow function expressions have a much cleaner syntax than function expressions and don’t have their own this, arguments, super, or new.target.
- The arrow function does not create its own this; it only inherits this from the upper level of its scope chain
interface Card {
suit: string;
card: number;
}
interface Deck {
suits: string[];
cards: number[];
createCardPicker(this: Deck): () = > Card;
}
const deck: Deck = {
suits: ["hearts"."spades"."clubs"."diamonds"].cards: Array(52),
// Even though the actual runtime is pointing to the deck object,
// However, typescript does static type checking and needs to know what this refers to before javascipt is compiled
// You must declare the type to which this refers in the function argument
createCardPicker: function (this: Deck) {
return () = > {
let pickedCard = Math.floor(Math.random() * 52);
let pickedSuit = Math.floor(pickedCard / 13);
return { suit: this.suits[pickedSuit], card: pickedCard % 13}; }; }};let cardPicker = deck.createCardPicker();
let pickedCard = cardPicker();
Copy the code
Function overloading
// overloads the function
function pick(input: string) :string;
function pick(input: number) :number;
// Only sting, number is allowed
// The last one, just a compatibility declaration,
function pick(input: unknown) :unknown {
// The input argument is a string
if (typeof input === "string") {
return "I am a string";
}
// The input parameter is a number
if (typeof input === "number") {
return input + 10;
}
/ / a Boolean value
return true;
}
let str: string = pick("hey");
let num: number = pick(2);
// Not in the overloaded type range, type error
let other: boolean = pick(true); // error
Copy the code
The generic
A ‘generic’ word is sufficient to indicate that it refers generally to a type range. In fact, generics are simply a matter of not specifying a specific type at first, then specifying the specific type through type parameters, and then specifying the type scope through type relationship processing.
Two key points about generics:
- Type parameters: The type is passed as a parameter to the corresponding generic declaration. Some generics provide default parameter types, which can be used without type parameters
- Type relationships: Types are passed to generics, which can be used directly or processed further to obtain a different type range
The main reason why generics are considered complex is the handling of type relationships. Different type parameters make the types change. However, clear above provide 2 points, actually also very good grasp.
// Use generics in the interface
/** * First of all, T is a type parameter. * T extends String means that the type parameter T satisfies string, thus constrains the input parameter */
interface Animal<T extends string> {
// Each key is of type string,
// Each element is an array of type T
[k: string]: T[];
}
// The string ABC is a subtype of string that complies with the T constraint on the type parameter
const dog: Animal<"abc"> = {
list: ["abc"]};// Use generics in class
/** * T is a type argument */
class GenericObject<T> {
// The zeroValue attribute has type T
zeroValue: T;
// The parameter x is of type T
// The parameter y is of type T
// Return value type T
add: (x: T, y: T) = > T;
}
// Assign type parameters to type number
const numObj: GenericObject<number> = {
zeroValue: 0,
add(x: number.y: number) :number {
returnx + y; }};Copy the code
Type the alias
Type alias: Essentially a name for a type declaration that is a reference to the type declaration. In official documentation, calling an alias gives the impression that a type already exists, just with a new name. Indeed, type aliases, in real development, extend existing types, recombine them, declare union or tuple types, and prefer aliases.
// Specify a declaration for the number type
type numRef = number;
const num: numRef = 10;
// Give the generic type declaration a name
type GenericType<T = number> = T;
type Tree<T = string> = {
value: T; left? : Tree<T>; right? : Tree<T>; };// Give a name to the recombination of existing types
type CompositeType = GenericType & Tree;
Copy the code
Interface VS type alias?
Interface and type aliases are syntactically similar. It is necessary to compare them side by side to deepen understanding. Type aliases are essentially type references, and they differ in two main ways.
- The use of a
The new name
Extensions to existing types are the same - Extends directly over the original type with the same name
A difference:
interface Animal {
name: string
}
// define a new interface Bear
interface Bear extends Animal {
honey: boolean
}
const bear = getBear()
bear.name
bear.honey
Copy the code
type Animal = {
name: string
}
// define a new type Bear
type Bear = Animal & {
honey: Boolean
}
const bear = getBear();
bear.name;
bear.honey;
Copy the code
Difference between 2:
interface Window {
title: string
}
// Merge interfaces
interface Window {
ts: import("typescript")}const src = 'const a = "Hello World"';
window.ts.transpileModule(src, {});
Copy the code
type Window = {
title: string
}
// Cannot merge, both names are called Window
type Window = {
ts: import("typescript")}// Error: Duplicate identifier 'Window'.
Copy the code
The TypScript document states that because interface is closer to javascript objects opening up to extensions, we recommend interfade if possible. On the other hand, if you can’t just use an interface to express the shape of a type, you need to use aliases to handle associative, meta-ancestor types, which we often do.
Interface: indicates suggestions for using type aliases
- Interface preferred: if it is a single interface, it may be extended and fit a single responsibility
- Preferred to use
Type the alias
: For the existing type extension, the emphasis is on the type relationship reconstruction to achieve the extension
conclusion
If you’re interested, take a look at typescript basics and try it out, focusing on the basics.