1. Does it work?

nature

  • Is a JavaScript with type annotations added
  • Does not break JavaScript’s original body of knowledge

More reliable

  • TypeScript’s static type detection can find and resolve low-level errors during development

Interface oriented programming

Such as:

interface UserInfo { name: string; age: number; avatar? : string; } function getUserInfo(props: UserInfo) { ... }Copy the code
  • Change your way of thinking and develop a good coding habit

The mainstream

  • React, Vue 3, Angular, Deno, Nest.js, etc., either source in TypeScript or provide perfect support for TypeScript.

2. Basic grammar

:A separator used to separate variables from types

let num: number = 6
Copy the code

Static type detection

Statically typed programming languages can accurately detect type errors at compile time, which is the advantage of static type checking.

Array type (Array)

Defines an array type using the form []

/** The child elements are arrays of numeric type */
let array1: number[] = [1.2.3];
/** The child element is an array of type string */
let array2: string[] = ['x'.'y'.'z'];
Copy the code

Use Array generics to define Array types

/** The child elements are arrays of numeric type */
let array1: Array<number> = [1.2.3];
/** The child element is an array of type string */
let array2: Array<string> = ['x'.'y'.'z'];
Copy the code

Tuple Type

The most important feature of tuples is that they can limit the number and type of array elements, which makes them particularly suitable for multi-value returns.

any

Any refers to an arbitrary type, which is an official cheat that selectively circumvents static type detection

unknown

Unknown is a type added in TypeScript 3.0 to describe variables of uncertain type. Temporary variables that can be used to receive different types of return values under different conditions

let result: unknown;
if (x) {
  result = x();
} else if (y) {
  result = y();
}
Copy the code

Unlike any, unknown is more secure on types. For example, we can assign a value of any type to unknown, but a value of unknown type can only be assigned to unknown or any

let result: unknown; let num: number = result; // prompt ts(2322) let anything: any = result; // No error will be displayedCopy the code

When unknown is used, TypeScript does type checking for it. However, without Type Narrowing, any operation we perform on Unknown will have the following error:

let result: unknown; result.toFixed(); / / hint ts (2571).Copy the code

All type reduction methods are valid for Unknown,

let result: unknown; if (typeof result === 'number') { result.toFixed(); // the hover result type is number.Copy the code

Void, undefined, null

Void, which applies only to functions that have no return value. That is, if the function returns no value, it is of type void.

never

Never indicates the type of value that never occurs

object

The object type is non-primitive type, that is, non-number, string, Boolean, bigint, symbol, NULL, undefined type

Type Assertion

Casting, like casting only at the type level, tells TypeScript to do type checking the way we do. Use the AS syntax for type assertion

const arrayNumber: number[] = [1, 2, 3, 4];
const greaterThan2: number = arrayNumber.find(num => num > 2) as number;
Copy the code

Use Angle bracket + type format for type assertion

const arrayNumber: number[] = [1, 2, 3, 4];
const greaterThan2: number = <number>arrayNumber.find(num => num > 2);
Copy the code

There is no difference between the two methods, but the Angle bracket format will cause syntactic conflicts with JSX, so as is preferred. Use the literal value + as const syntax structure for constant assertions

/** let STR = 'STR' as const; /** readOnlyArr is' readOnlyArr [0, 1]' */ const readOnlyArr = [0, 1] as const;Copy the code

A special nonempty assertion that adds ‘! To the end of a value (variable, attribute) ‘assertion operator, which can be used to exclude null, undefined

let mayNullOrUndefinedOrString: null | undefined | string; mayNullOrUndefinedOrString! .toString(); // ok mayNullOrUndefinedOrString.toString(); // ts(2531) // For non-empty assertions, we should also consider it as dangerous as any.Copy the code

3. Literal typing, type inference, type widening, and type narrowing

Type inference

{ let x1 = 42; Let x2: number = x1; // ok }Copy the code

Context inference

Type Adder = (a: number, b: number) => number; const add: Adder = (a, b) => { return a + b; } const x1 = add(1, 1); Const x2 = add(1, '1'); // ts(2345) Argument of type '"1"' is not assignable to parameter of type 'number }Copy the code

Literal type

In TypeScript, literals can represent not only values but also types, known as literal types.

Currently, TypeScript supports three literal types: string literals, number literals, and Boolean literals, each of which has the same literal type as its value

{
 let specifiedStr: 'this is string' = 'this is string';
 let specifiedNum: 1 = 1;
 let specifiedBoolean: true = true;
}
Copy the code

A literal type is a subtype of a collection type, which is a more concrete representation of the collection type. For example, ‘this is string’ (which represents a string literal type) is of type string (or rather a subtype of string), And string doesn’t have to be the ‘this is String ‘type

{ let specifiedStr: 'this is string' = 'this is string'; let str: string = 'any string'; specifiedStr = str; // ts(2322) type '"string"' cannot be assigned to type 'this is string' STR = specifiedStr; // ok }Copy the code

String literal type

Use a string literal type as the type of the variable

let hello: 'hello' = 'hello';
hello = 'haha'; // ts(2322) Type '"haha"' is not assignable to type '"hello"'
Copy the code

The application scenario is that multiple literal types can be combined into a single union type to describe a practical collection with explicit members

type Direction = 'up' | 'down';
function move(dir: Direction) {
  // ...
}
move('up'); // ok
move('left'); // ts(2345) Argument of type '"left"' is not assignable to parameter of type 'Direction'
Copy the code

Numeric literal types and Boolean literal types

/ / the joint type of the function of using a combination of literal parameters are limited to a more specific type interface Config {size: 'small' | 'big'; isEnable: true | false; margin: 0 | 2 | 4; }Copy the code

Literal design (Refinement of Literal types)

All variables defined by let or var, function parameters, and non-read-only attributes of an object that meet the conditions for specifying an initial value and not explicitly adding a type annotation, infer the type of the specified initial value literal-type broadening. This is literal-type broadening.

{ let str = 'this is string'; // Type is string const specifiedStr = 'this is string'; // The type is 'this is string'}Copy the code

Type KP

To broaden the null, and undefined type, through the let, var variables defined if meet not explicitly declared type annotation and endowed with null or undefined values, then deduce the type of these variables is any:

{ let x = null; // let y = undefined; / / type to broaden into any / * * -- -- -- -- -- line -- -- -- -- -- -- -- * / const z = null; // Type is null}Copy the code

Type Narrowing.

Reduce the type of a variable from a broad set to a relatively small, unambiguous set by some operation

You can use type guards to narrow the type of a function parameter from any to an explicit type

{ let func = (anything: any) => { if (typeof anything === 'string') { return anything; Else if (typeof anything === 'number') {return anything; // Type is number} return null; }; }Copy the code

4. Function types

Return value type

The value => in a function type is used to represent the definition of the function. The parameter type of the function is on the left and the return value type of the function is on the right

type Adder = (a: number, b: number) => number; Const add: Adder = (a, b) => a + b; // ES6 arrow functionCopy the code

In addition to using this declaration syntax in objects, we can also use a shorthand syntax similar to object attributes to declare properties of function types

interface Entity { add: (a: number, b: number) => number; del(a: number, b: number): number; } const entity: Entity = { add: (a, b) => a + b, del(a, b) { return a - b; }};Copy the code

Default and inferred return value types

function computeTypes(one: string, two: number) { const nums = [two]; Const STRS = [one] return {nums, STRS} // Return {nums: number[]; STRS: string[]}Copy the code

Type inference of function return values combined with generics enables particularly complex type calculations, such as the association of State, Reducer, and Effect types in the Redux Model.

The return value of the Generator function

Generator functions return an Iterator object. We can use generics of Generator’s name or Iterator’s name to indicate the type of the returned value (Generator types inherit from Iterator types).

type AnyType = boolean; type AnyReturnType = string; type AnyNextType = number; function *gen(): Generator<AnyType, AnyReturnType, AnyNextType> { const nextValue = yield true; Return '${nextValue}'; // Mandatory string type}Copy the code

Optional and default parameters

function log(x? : string) { return x; } log(); // => undefined log('hello world'); // => hello worldCopy the code

Add? Before: of the type annotation. The parameter x representing the log function is defaultable.

The default parameter type of a function must be a subtype of the parameter type

function log3(x: number | string = 'hello') {
    console.log(x);
}
Copy the code

The remaining parameters

function sum(... nums: number[]) { return nums.reduce((a, b) => a + b, 0); }Copy the code

this

Declare the object to which this refers (how the function is called) in the first argument to the function, as in the simplest case, the this pointer to the object’s method

function say(this: Window, name: string) {
    console.log(this.name);
}
window.say = say;
window.say('hi');
const obj = {
    say
};
obj.say('hi'); // ts(2684) The 'this' context of type '{ say: (this: Window, name: string) => void; }' is not assignable to method's 'this' of type 'Window'.
Copy the code

Note that if we call say() directly, this would actually point to the global variable window, but TypeScript doesn’t know who called say, so we default this to void, which causes a TS (2684) error.

When defining a function property of an object, TypeScript can detect errors as long as the reference to this is different in the actual call

interface Person { name: string; say(this: Person): void; } const person: Person = { name: 'captain', say() { console.log(this.name); }}; const fn = person.say; fn(); // ts(2684) The 'this' context of type 'void' is not assignable to method's 'this' of type 'Person'Copy the code

Note: Explicitly annotating functions with this, which ostensibly takes the place of the first parameter, doesn’t mean the function actually has an extra parameter because TypeScript translations to JavaScript erase the “pseudo-parameter” this. This is one of TypeScript’s few unique syntax features.

Function overloading

function convert(x: P2): string;
function convert(x: P1): number;
function convert(x: P1 | P2): any { }
const x1 = convert({ name: '' } as P1); // => number
const x2 = convert({ name: '', age: 18 } as P2); // => string
Copy the code

Type predicate (IS)

Return typeof s === 'string'; return typeof s === 'string'; } function isNumber(n: number) { return typeof n === 'number'; } function operator(x: Unknown) {if(isString(x)) {// ok x type is reduced to string} if(isNumber(x)) {// ts(2345) unknown cannot be assigned to number}}Copy the code

5. Class type

class

class Dog { name: string; constructor(name: string) { this.name = name; } bark() { console.log('Woof! Woof! '); } } const dog = new Dog('Q'); dog.bark(); // => 'Woof! Woof! 'Copy the code

inheritance

Using the extends keyword makes it easy to define an abstract schema for class inheritance

class Animal { type = 'Animal'; say(name: string) { console.log(`I'm ${name}! `); } } class Dog extends Animal { bark() { console.log('Woof! Woof! '); } } const dog = new Dog(); dog.bark(); // => 'Woof! Woof! ' dog.say('Q'); // => I'm Q! dog.type; // => AnimalCopy the code

Public, private, and protected modifiers

There are three access modifiers supported in TypeScript: public, private, and protected.

  • Public modifies properties or methods that are public and visible anywhere;

  • Private modifies properties or methods that are visible only in the same class and are private;

  • Protected modifies the properties or methods that are visible and protected only in the class itself and its subclasses.

Read-only modifier

Declare attributes of a class using the readonly read-only modifier

class Son {
  public readonly firstName: string;
  constructor(firstName: string) {
    this.firstName = firstName;
  }
}
const son = new Son('Tony');
son.firstName = 'Jack'; // ts(2540) Cannot assign to 'firstName' because it is a read-only property.
Copy the code

accessor

You can intercept read and write access to class members through getters and setters.

Static attributes

You can define static properties and methods for a class.

These properties exist on a particular object, the class, rather than an instance of the class, so we can access static properties directly from the class,

class MyArray {
  static displayName = 'MyArray';
  static isArray(obj: unknown) {
    return Object.prototype.toString.call(obj).slice(8, -1) === 'Array';
  }
}
console.log(MyArray.displayName); // => "MyArray"
console.log(MyArray.isArray([])); // => true
console.log(MyArray.isArray({})); // => false
Copy the code

Note that methods that do not rely on the instance this context can be defined as static methods, which means that you need to explicitly annotate this to use this in static methods; Non-static methods do not need to explicitly annotate this, because this points to an instance of the class by default.

An abstract class

A special class that cannot be instantiated and can only be inherited by subclasses.

abstract class Adder { abstract x: number; abstract y: number; abstract add(): number; displayName = 'Adder'; addTwice(): number { return (this.x + this.y) * 2; } } class NumAdder extends Adder { x: number; y: number; constructor(x: number, y: number) { super(); this.x = x; this.y = y; } add(): number { return this.x + this.y; } } const numAdder = new NumAdder(1, 2); console.log(numAdder.displayName); // => "Adder" console.log(numAdder.add()); // => 3 console.log(numAdder.addTwice()); / / = > 6Copy the code

The type of the class

The type of a class is similar to that of a function, that is, when a class is declared, it also declares a special type (specifically, an interface type). The name of the type is the class name, indicating the type of the class instance. When we define a class, we declare all property and method types other than constructors as members of this special type.

class A {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
}
const a1: A = {}; // ts(2741) Property 'name' is missing in type '{}' but required in type 'A'.
const a2: A = { name: 'a2' }; // ok
Copy the code

6. Interface type and type alias

Interface Interface type

/** ProgramLanguage */ ProgramLanguage {/** ProgramLanguage */ name: string; /** age: () => number; }Copy the code

Defaultable property

/** OptionalProgramLanguage {/** OptionalProgramLanguage */ name: string; /** ** age? : () => number; } let OptionalTypeScript: OptionalProgramLanguage = { name: 'TypeScript' }; // okCopy the code

Read-only property

Interface ReadOnlyProgramLanguage {/** language name */ readonly name: string; Use fixed number of year / * * * / readonly age: (() = > number) | undefined; } let ReadOnlyTypeScript: ReadOnlyProgramLanguage = { name: 'TypeScript', age: } /** ts(2540) error, name only */ ReadOnlyTypeScript. Name = 'JavaScript';Copy the code

Defining function types

Interface StudyLanguage {(language: ProgramLanguage): void} /** StudyLanguage = language => console.log(`${language.name} ${language.age()}`);Copy the code

The index sign

interface LanguageRankInterface { [rank: number]: string; } interface LanguageYearInterface { [name: string]: number; } { let LanguageRankMap: LanguageRankInterface = { 1: 'TypeScript', // ok 2: 'JavaScript', // ok 'WrongINdex': '2012' // ts(2322) non-existent attribute name}; let LanguageMap: LanguageYearInterface = { TypeScript: 2012, // ok JavaScript: 1995, // ok 1: 1970 // ok }; }Copy the code

Note: In the example above, when a number is used as an object index, its type is compatible with both numbers and strings, which is consistent with JavaScript behavior. Therefore, when you index an object with 0 or ‘0’, the two are equivalent.

{ interface LanguageRankInterface { readonly [rank: number]: string; } interface LanguageYearInterface { readonly [name: string]: number; }} // LanguageRankInterface and LanguageYearInterface Any numeric or string attribute is read-only.Copy the code

Inheritance and Implementation

Interface inheritance can be implemented using the extends keyword as shown below.

{ interface DynamicLanguage extends ProgramLanguage { rank: number; } interface TypeSafeLanguage extends ProgramLanguage {typeChecker: string; } /** inherits multiple */ interface TypeScriptLanguage extends DynamicLanguage, TypeSafeLanguage {name: 'TypeScript'; // Redefine the property with compatible types of the original property type (such as subsets)}}Copy the code

Note: We can only override inherited properties with compatible types

{/** TS (6196) error inheritance, name attribute incompatible */ interface WrongTypeLanguage extends ProgramLanguage {name: number; }} // Because the name attribute of ProgramLanguage is string and the name attribute of WrongTypeLanguage is number, the two are incompatible, so they cannot be inherited and a TS (6196) error will be displayed.Copy the code

Type Type alias

/** Type alias */ {type LanguageType = {/** The following is the interface attribute */ /** language name */ name: string; /** age: () => number; }}Copy the code

For scenarios that cannot be covered by interface types, such as composite types and cross types, we can only receive them using type aliases

{/ * * * / type MixedType = string | number; /** intersecting */ type IntersectionType = {id: number; name: string; } & { age: number; name: string }; / / ProgramLanguage['age']; }Copy the code

Difference between Interface and Type

  • Interface types are defined repeatedly, and their attributes are superimposed, which makes it extremely easy to extend global variables and third-party library types
{
  interface Language {
    id: number;
  }
  
  interface Language {
    name: string;
  }
  let lang: Language = {
    id: 1, // ok
    name: 'name' // ok
  }
}
Copy the code
  • Defining the type alias repeatedly, as shown in the code below, prompts a TS (2300) error
{/** ts(2300) repeat flag */ type Language = {id: number; } /** ts(2300) repeated flag */ type Language = {name: string; } let lang: Language = { id: 1, name: 'name' } }Copy the code

7. Advanced types: union types and cross types

The joint type

The type of a variable or parameter is not a single atomic type, but may be a combination of different types.

Through “|” operator separate types of grammar to represent the joint type

function formatPX(size: number | string) { // ... } formatPX(13); // ok formatPX('13px'); // ok formatPX(true); / / ts (2345) 'true' type cannot give 'number | string' type formatPX (null); / / ts (2345) 'null' type cannot give 'number | string' typeCopy the code

We can combine any type and any type to construct a type that better meets our needs

function formatUnit(size: number | string, unit: 'px' | 'em' | 'rem' | '%' = 'px') {
  // ...
}
formatUnit(1, 'em'); // ok
formatUnit('1px', 'rem'); // ok
formatUnit('1px', 'bem'); // ts(2345)
Copy the code

You can use a type alias to pull away from an upper union type and then join it further

type ModernUnit = 'vh' | 'vw'; type Unit = 'px' | 'em' | 'rem'; type MessedUp = ModernUnit | Unit; / / type is' the vh '|' vw '|' px '|' em '|' rem 'Copy the code

Interface types can be combined to represent more complex structures

interface Bird { fly(): void; layEggs(): void; } interface Fish { swim(): void; layEggs(): void; } const getPet: () => Bird | Fish = () => { return { // ... } as Bird | Fish; }; const Pet = getPet(); Pet.layEggs(); // ok Pet.fly(); // ts(2339) 'Fish' has no 'fly' attribute; 'Bird | Fish' not 'fly' attribute / / in joint type, we can direct access to each member has the interface properties, methods, and do not prompt type errors. However, if the attributes and methods are unique to individual members, we need to treat them differently. At this time, we need to introduce type guards to distinguish different member types.Copy the code

Type guards are required based on the IN operator

if (typeof Pet.fly === 'function') { // ts(2339)
  Pet.fly(); // ts(2339)
}
if ('fly' in Pet) {
  Pet.fly(); // ok
}
Copy the code

Cross type

To combine multiple types into a single type, the combined type has the characteristics of all member types

Merging interface types

The real use of federated interface types is to combine multiple interface types into one type to achieve the same effect as interface inheritance, known as merged interface types

  type IntersectionType = { id: number; name: string; } 

    & { age: number };

  const mixed: IntersectionType = {

    id: 1,

    name: 'name',

    age: 18

  }

Copy the code

If the name attribute type is incompatible, such as in the example above where the name attribute type of the two interface types is number and string, the name attribute type is the intersection of the atomic types number and string, that is, never

type IntersectionTypeConfict = { id: number; name: string; } & { age: number; name: number; }; Const mixedConflict: IntersectionTypeConfict = {id: 1, name: 2, // ts(2322) error, 'number' cannot be assigned to 'never' age: 2};Copy the code

If an attribute of the same name has a compatible type, such as number, a subtype of number, or a numeric literal type, then the merged name attribute is a subtype of either.

type IntersectionTypeConfict = { id: number; name: 2; } & { age: number; name: number; }; let mixedConflict: IntersectionTypeConfict = { id: 1, name: 2, // ok age: 2 }; MixedConflict = {id: 1, name: 22, // '22' cannot be assigned to '2' age: 2};Copy the code

Merge union type

You can merge a union type into a cross type that satisfies the different union type constraints, that is, the members of the same type that are extracted from all union types. Here, we can also think of merging union types as finding an intersection.

  type UnionA = 'px' | 'em' | 'rem' | '%';

  type UnionB = 'vh' | 'em' | 'rem' | 'pt';

  type IntersectionUnion = UnionA & UnionB;

  const intersectionA: IntersectionUnion = 'em'; // ok

  const intersectionB: IntersectionUnion = 'rem'; // ok

  const intersectionC: IntersectionUnion = 'px'; // ts(2322)

  const intersectionD: IntersectionUnion = 'pt'; // ts(2322)

Copy the code

Union, cross combination

Joint operator | priority & below the cross operator, also, we can through the use of the small bracket () to adjust the priority of the operators.

type UnionIntersectionA = { id: number; } & { name: string; } | { id: string; } & { name: number; }; / / crossover operator precedence above joint operators type UnionIntersectionB = (' px '|' em '|' rem '|' % ') | (' the vh '|' em '|' rem '|' pt); // Adjust the priorityCopy the code

Furthermore, we can also introduce basic rules such as distributive ratio and commutative law into type combinations and then optimize for cleaner and cleaner types,

type UnionIntersectionC = ({ id: number; } & { name: string; } | { id: string; }) & { name: number; }; type UnionIntersectionD = { id: number; } & { name: string; } & { name: number; } | { id: string; } & { name: number; }; Type UnionIntersectionE = ({id: string; } | { id: number; } & { name: string; }) & { name: number; }; // It satisfies the commutative lawCopy the code

Type of cut

What is the effect of combining a string primitive type with a string literal type into a joint type? The effect is that the type is reduced to string

type URStr = 'string' | string; / / type is string type URNum = 2 | number; / / type is number type URBoolen = true | Boolean; / / type is a Boolean enum EnumUR {ONE, TWO} type URE = EnumUR. ONE | EnumUR; // The type is EnumURCopy the code

Reduced, but greatly reduced the ability of the IDE to autoprompt,

type BorderColor = 'black' | 'red' | 'green' | 'yellow' | 'blue' | string; In the code above, we wanted the IDE to automatically prompt for annotated string literals, but since the type was reduced to string, all string literals black, red, and so on were not automatically prompt. TypeScript also provides a dark magic that allows type reduction to be controlled. As shown in the following code, we only need to add to the parent type "&" {} can type BorderColor = 'black' | 'red' | 'green' | 'yellow' | 'blue' | string & {}; // All literal types are preservedCopy the code

8. Enumeration types

Enumerated type

  enum Day {

    SUNDAY,

    MONDAY,

    TUESDAY,

    WEDNESDAY,

    THURSDAY,

    FRIDAY,

    SATURDAY

  }  

Copy the code

There are seven common enumeration types:

Numeric types, string types, heterogeneous types, constant and computed (value) members, enumerator and joint enumerations, constant enumerations, external enumerations.

  • Numeric enumerations: By specifying only constant names, we define a set of numbers that starts incrementing from 0 by default, called numeric enumerations. If we want the enumeration value to increment from another value, we can display the initial value of the specified enumeration member in the format “constant name = value”
  enum Day {

    SUNDAY = 1,

    MONDAY,

    TUESDAY,

    WEDNESDAY,

    THURSDAY,

    FRIDAY,

    SATURDAY

  }

Copy the code

Assign SUNDAY to any type (such as integer, negative, decimal, etc.), starting with any number, and incrementing members that do not display the specified value by 1

  • String enumerations: Enumerations that define values as string literals are called string enumerations, and string enumerations retain those values after being translated into JavaScript
  enum Day {

    SUNDAY = 'SUNDAY',

    MONDAY = 'MONDAY',

    ...

  }

Copy the code
  • Heterogeneous enums: TypeScript supports enumeration types that have both numeric and character members. Such enumerations are called Heterogeneous enums.

  • Constant and computed (value) members: In the previous example, the enumerated members involved, whose values are strings, numeric literals, and numeric constants incrementing from 0 with an unspecified initial value, are referred to as constant members. In translation, members that define values through evaluated constant enumeration expressions are also called constant members, as in the following cases:

    • References come from a predefined constant member, such as from the current enumeration or another enumeration;

    • Constant enumeration expression wrapped in parentheses ();

    • The unary operators +, -, ~ applied to constant enumeration expressions;

    • Constant enumeration operation expression of binary operators +, -, *, /, %, < <, > >, > > >, &, |, ^.

    Except in these cases, all others are considered calculated members.

    Enum FileAccess {/ / constant member None, Read = 1 < < 1, the Write = 1 < < 2, ReadWrite = Read | Write, / / calculated members G = "123". The length,}Copy the code
  • Enumeration member types and union enumerations

    The relationship between an enumeration member and an enumeration type can be described in two ways: if the enumeration member contains both literal and non-literal enumeration values, the type of the enumeration member is the enumeration itself (the enumeration type is itself a subtype); If enumerators are all literal enumerations, then all enumerators are both values and types

    enum Day { SUNDAY, MONDAY, } enum MyDay { SUNDAY, MONDAY = Day.MONDAY } const mondayIsDay: Day.MONDAY = Day.MONDAY; // ok: the literal enumerator is both a value and a type const mondayIsSunday = myday.sunday; // ok: the type is MyDay, myday. SUNDAY is just the value const mondayIsMyDay2: myday. MONDAY = myday. MONDAY; // ts(2535), MyDay contains non-literal-valued members, so myday.monday cannot be used as a typeCopy the code
  • Constant enumeration (const enums)

    const enum Day { SUNDAY, MONDAY } const work = (d: Day) => { switch (d) { case Day.SUNDAY: return 'take a rest'; case Day.MONDAY: return 'work hard'; }}Copy the code
  • External enumeration (Ambient enums) : Describes a variable that has been defined elsewhere by declare

    declare let $: any;
    $('#id').addClass('show'); // ok
    Copy the code
  • The differences between external enumerations and regular enumerations are the following:

    • In an external enumeration, members that do not specify an initial value are treated as calculated (value) members, as opposed to regular enumerations.

    • Even if an external enumeration contains only literal members, those members are not of a literal member type, and naturally do not have all the features of a literal type.

9. The generic

Generics refer to type parameterization, that is, parameterization of a specific type. As with function parameters, we can define several type parameters for a generic type and pass them explicit type parameters when called. Generics are designed to effectively constrain the relationships between type members, such as function parameters and return values, or between class or interface members and methods.

Generic type parameters

function reflect<P>(param: P) {
  return param;
}
Copy the code

Generics can not only restrict the type of the whole parameter of a function, but also restrict the type of the parameter properties and members. For example, the type of the parameter can be array, object

function reflectArray<P>(param: P[]) {
  return param;
}
const reflectArr = reflectArray([1, '1']); // reflectArr 是 (string | number)[]
Copy the code

A generic class

In class definitions, we can also use generics to constrain the types of constructors, properties, and methods

class Memory<S> { store: S; constructor(store: S) { this.store = store; } set(store: S) { this.store = store; } get() { return this.store; } } const numMemory = new Memory<number>(1); // <number> default const getNumMemory = nummemory.get (); // The type is number nummemory.set (2); // Can only write number const strMemory = new Memory(" "); // Default <string> const getStrMemory = strmemory.get (); // The type is string strmemory.set ('string'); // Only string can be writtenCopy the code

The generic type

const reflectFn: <P>(param: P) => P = reflect; // ok
Copy the code
type ReflectFuncton = <P>(param: P) => P;
interface IReflectFuncton {
  <P>(param: P): P
}
const reflectFn2: ReflectFuncton = reflect;
const reflectFn3: IReflectFuncton = reflect;
Copy the code

Note: Enumeration types do not support generics.

Generic constraint

Restrict generic input parameters to a relatively unambiguous set to constrain the input parameters.

function reflectSpecified<P extends number | string | boolean>(param: P):P { return param; } reflectSpecified('string'); // ok reflectSpecified(1); // ok reflectSpecified(true); // ok reflectSpecified(null); / / ts (2345) 'null' not give type 'number | string | Boolean'Copy the code