preface
To get ready for the release of vUE 3.0, learn a new typescript stream before the volume of code in the repository starts to swell. I first learned typescript when I first started working on the front end in 2017, just reading a few blogs about it. I learned typescript for the second time in 2018, the second year of my work. My understanding and use of TS was limited to basic functions such as type checking, and I had no idea about some advanced concepts in TS. This study is to prepare for a systematic study and summary of TS. By the way, some of the typescript Chinese documentation is already out of date. For those of you who are good at English, read the TS website. For outdated content on TS Chinese, I use the emoji”👻” to mark it. This article is a note I took when I was learning TS.
The base type
/ / a Boolean
const isCheck: boolean = false
/ / digital
const year: number = 2077
/ / string
const name: string = 'Cyberpunk'
/ / array
const list: number[] = [1.2.3]
const list: Array<number> = [1.2.3]
Copy the code
Tuple
The Tuple represents an array of known elements and types. Ts throws an error when accessing a crossover element.
👻 : In typescript’s Chinese language, access cross-boundary elements and use federated types. It’s outdated content.
const tuple: [number.string] = [2077.'Cyberpunk']
Copy the code
Enum
You can provide more semantic names for a set of data
enum language {CPP = 1, JS = 2, JAVA = 3}
const lan: language = language.CPP / / 1
// Enumeration type. You can also obtain the enumeration name from the enumeration value
const lanName: string = language[2] / / 2
Copy the code
Any
Any specifies a type for variables whose type is unknown, and variables will not be typed.
Void
Void means there is no type. If a variable is of type Void, only undefined or null can be assigned.
Null & Undefined
Null, undefined is a subtype of any type. Null and undefined can be assigned to number or string.
If the tsconfig.json configuration item strictNullChecks is set to true, then null, undefined can only be assigned to their respective types and Void types.
Never
Never indicates a type that Never exists. For example, a function always throws an error and returns no value. Or a function that has an infinite loop inside that never returns a value. The return value of this function is of type Never.
Object
Object represents a non-primitive type, that is, a type other than number, string, Boolean, symbol, NULL, or undefined. For example, if a function takes an object as a parameter and the key and value of the parameter are indeterminate, the parameter can be of type Object.
Types of assertions
Type assertions tell the compiler that we know exactly what type a variable is, without type checking.
// as syntax or Angle bracket syntax
alert((temp as string).length)
alert((<string>temp).length)
Copy the code
Double assertion
S as T, the assertion can only succeed if S is a subset of T or T is a subset of S
window.onclick = function (event: Event) {
let mouseEvent = event as HTMLElement // Error, Event, HTMLElement types are not parent-child sets
mouseEvent = (event as any) as HTMLElement // ok
}
Copy the code
interface
The main purpose of an interface is to name types. The type checks the structure of the value. In TS you don’t say, “This object implements this interface.” You only care about the shape of the value, and as long as the object satisfies the shape of the value of the interface, then it is allowed.
interface Bar { name: string }
interface Foo { name: string }
const bar: Bar = { name: 'Geralt' }
const foo: Foo = { name: 'V' }
function getName (hero: Bar) { return hero.name }
// Hero accepts the Bar type, but Foo and Bar are identical in structure, so using Foo is allowed
getName(bar) // Geralt
getName(foo) // V
Copy the code
Optional attribute
Is the optional attribute added after the attribute defined by the interface? , indicating that some properties in an object exist only under certain conditions or not at all.
Read-only property
Read-only attributes add the readonly keyword to the attributes defined by the interface. Once a read-only property is created, it cannot be modified.
A read-only array
Use the ReadonlyArray keyword to declare a read-only array. But we can bypass the compiler by modifying read-only arrays with type assertions.
let list: ReadonlyArray<number> = [1.2.3]
// error
list.push(4)
// ok
(list as number[]).push(4)
Copy the code
Additional property checking
Object literals undergo additional property checks, and the compiler throws an error if the object literals have properties that do not exist in the target type.
interface Bar { name: string, age? :number }
function getName (hero: Bar) { return hero.name }
// error, object literals perform additional property checks
getName({ name: 'Geralt', gender: 'man' })
// ok, using type assertions bypasses the check
getName({ name: 'Geralt', gender: 'man' } as Bar)
// ok, use a variable that does not perform additional attribute checking
const bar = { name: 'Geralt', gender: 'man' }
getName(bar)
// Or modify the interface definition to use index signatures for unknown attributes
Copy the code
Function types
Interfaces can also describe functions. The parameter name of the function implementing the interface does not need to match the parameter name defined by the interface.
interface FooFunc {
(a: number, b: string) :string
}
const foo: FooFunc = (a: number, b: string) :string= > a + b
// If you do not specify a type, TypeScript automatically deduces the type of the parameter and return value
const far: FooFunc = (a, b) = > a + b
Copy the code
Indexable type
Indexable types have an index signature that describes the type of the object index and the corresponding index return value type. TypeScript supports two index types, strings and numbers. Both index types can be used in code, but the type of the numeric index return value must be a subtype (subset) of the string index. Because Foo[‘1’] and Foo[1] are identical.
interface Foo {
[key: number] :string,
[key: string] :string
}
interface NumberDictionary {
[index: string] :number;
length: number; // ok
name: string; // error
}
interface Foo {
[key: string] :number | string;
length: number; // ok
name: string; // ok
}
Copy the code
The nesting of indexable types
To take a quick example, the following indexable types are not safe
interfaceFoo { color? :string
[key: string] :string
}
// Because of a clerical error, the spelling was wrong, color -> colour, but because of indexable type, this does not throw an error
let a: Foo = {
colour: 'blue'
}
Copy the code
The logical way to think about it is to separate indexable types into separate attributes
interfaceFoo { color? :stringother? : { [key:string] :string}}// The error compiler throws an error
let a: Foo = {
colour: 'blue'
}
Copy the code
Class types
TypeScript uses interfaces to force instance parts of a class to implement properties or methods. But for static parts, such as static properties and methods, constructor is outside the scope of inspection.
interface WitcherClass {
name: string; // The interface can define attributes of 'instances'
getName(): string; // The interface can define methods of 'instances'
}
The Witcher class implements the WitcherClass interface
class Witcher implements WitcherClass {
name: string;
getName () { return this.name }
constructor() {}}Copy the code
Inherited interface
Interfaces can be inherited using the extends keyword. An interface can inherit multiple interfaces at the same time to achieve a composite interface.
interface Witcher { name: string; }
interface Wolf extends Witcher { fencing: string }
const geralt: Wolf = {
name: 'geralt'.fencing: 'New Year's Greetings with swords'
}
Copy the code
Interface inheritance class
An interface can inherit from a class, but only the definition of the class, not the implementation of the class. The interface inherits the modifiers of the private and protected members of the class. When an interface inherits from a class that contains private and protected members, the interface can only be implemented by that class and its subclasses.
class Foo { protected name: string }
interface InterfaceFoo extends Foo { getName(): string }
// ok
class Bar extends Foo implements InterfaceFoo {
getName() { return this.name }
}
// error, Far has no name attribute, only Foo and its subclasses
class Far implements InterfaceFoo {
getName() { return this.name }
}
Copy the code
class
The modifier
- Public Default modifier. Members of a TypeScript class default to public
- Members of a private class cannot be accessed outside the class, nor can subclasses
- Members of protected classes are not accessible from outside the class, but from subclasses. If the constructor of a class is protected, the class can only be inherited, not instantiated.
- Readonly keyword
readonly
You can set the property of an instance to read-only
Parameter properties
Use private, protected, public, or readonly in constructor arguments to combine declaration and assignment
// Boo, Far declares the name attribute in the same way
class Boo {
public name: string
constructor (theName: string) {
this.name = theName
}
}
class Far {
constructor (public name: string) {}}Copy the code
getters/setters
You can use getters/setters to control reading and writing to class members, but if members of a class have only GET, they are read-only by default.
class Boo {
// control the getters/setters behavior of _name
private _name: string
get name(): string {
return this._name
}
set name (newName): void {
if (newName) {
this._name = newName
}
}
}
Copy the code
Static properties/static methods
Use the static keyword to define a class’s static properties or methods, using the class name.
class Boo {
static lan: string = 'JAVA'
static getLan () {
return Boo.lan
}
}
// JAVA
console.log(Boo.getLan())
Copy the code
An abstract class
Define abstract classes using the abstract keyword. Abstract classes can only be inherited, not directly instantiated. Abstract can also be used in abstract classes to define abstract methods, which do not contain concrete implementations and must be implemented in subclasses.
abstract class Boo {
public name: string = 'boo'
public abstract getName(): string
}
class Bar extends Boo {
// Subclasses must implement abstract methods of the abstract class
getName () {
return this.name
}
}
Copy the code
typeof
The type of the instance declared when declaring a class in TypeScript. Using typeof class names takes the typeof the class itself.
class Bar {
static lan: string = 'Java'
constructor (public age: number) {}}const a: Bar = new Bar(20)
// typeof class name, which retrieves the typeof the class itself, including static members and constructors
const b: typeof Bar = Bar
// Java
console.log(b.lan)
Copy the code
Private versus singleton mode
class Bar {
private static instance: Bar
private constructor () {}
public static create() {
if(! Bar.instance) { Bar.instance =new Bar()
}
returnBar.instance; }}let a = new Bar() // error, the constructor cannot be accessed outside the class
let b = Bar.create() // ok
Copy the code
function
Function types
// Function type
const sum = (a: number, b: number) :number= > a + b
// Full function type. Parameter names can be different
const sum: (a: number, b: number) = > number = (x: number, y: number) = > x + y
Copy the code
Inference type
// The ts compiler will automatically infer the type of x, y, and the return value of the function
const sum: (a: number, b: number) = > number = (x, y) = > x + y
Copy the code
Optional parameters, default parameters, remaining parameters
// Optional argument, use '? 'Realize optional parameter function, optional parameter must be at the end of the required parameter
constsum = (a: number, b? : number):number= > {
if (b) {
return a
} else {
return a + b
}
}
// The default value of the parameter. If the parameter value is undefined, the default parameter will be used
const sum = (a: number, b: number = 0) :number= > a + b
// The remaining parameters, using ellipsis to define the array of remaining parameters
const sum = (.arguments: number[]) = > {
return arguments[0] + arguments[1]}Copy the code
This parameter
This appears in the function’s object literal, which defaults to type any. You can add the this argument to a function, which is a dummy argument, first in the argument list
interface Bar {
name: string
}
function foo () {
return {
name: this.name // This is of type any}}function bar (this: Bar) {
return {
name: this.name // This is of type Bar}}Copy the code
overloading
Function overloading can be used when we need to return different types of results based on different types of arguments. Provide multiple function type definitions for the same function for function overloading. The compiler handles function calls based on this list. The compiler looks through the list of overloads in turn to find matching function definitions.
Function foo(x: number): string function foo(x: string): number // function foo(x): any any { if (typeof x === 'number') { return x + '' } else if (x === 'string') { return Number(x) } }Copy the code
The generic
Use generics to create reusable components that can support multiple data types
// The echo function is generic
function echo<T> (arg: T) :T {
return arg
}
// Explicitly pass in type parameters
echo<string> ('Hello')
// Using type inference for ts, the compiler automatically verifies the type of T based on the argument
echo('Hello')
Copy the code
Generic variables
For a generic parameter, we must treat it as any or all types.
function echo<T> (arg: T) :number {
/ / error. Boolean, numeric types do not have length attributes
return arg.length
}
Copy the code
A generic interface
interface Foo {
<T>(arg: T): T
}
const foo: Foo = <T>(arg: T): T= > arg
// Specify the generic type by treating the generic parameter as an interface parameter
interface Foo<T> {
(arg: T): T
}
// Arg will be derived to type number
const foo: Foo<number> = (arg) = > arg
Copy the code
A generic class
Classes are divided into static parts and instance parts. Generic classes refer to the types of instance parts and cannot be used for static parts
class Foo<T, U> {
static name:T // error, static members cannot be generic types
constructor(public x: T, public y: U) {}
getY(): U { return this.y }
getX(): T { return this.x }
}
const foo = new Foo('CyberpuCk'.2077)
Copy the code
Generic constraint
interface NameConstraint {
name: string
}
// Constrain the generic parameter T to contain the name attribute, not any type
function witcher<T extends NameConstraint> (people: T) :T {
return people
}
// ok
witcher({ name: 'geralt' })
// error, must have the name attribute
witcher('geralt')
Copy the code
Use class types in generics
Use the new (… Any: []) => T, reference the class type
Function create<T>(c: new(... any[]) => T): T { return new c(); }Copy the code
Use generics wisely (with examples from Axios)
import axios from 'axios'
// A generic return structure
// Use generics to encapsulate a common interface for returning data
interface ResponseData<T = any> {
code: number;
result: T;
message: string;
}
// Encapsulate the request function
// Request user data
function getUser<T> () {
return axios.get<ResponseData<T>>('/user').then((res) = > {return res.data
}).catch(error => { }} // Request a set of user datafunction getUsers<T>(a) {
return axios.get<ResponseData<T> > ('/users').then((res) = > {return res.data
}).catch(error => { })}Copy the code
// User interface
interface User {
id: string,
name: string
}
/ / use
async function test () {
await getUser<User>()
await getUsers<User[]>()
}
Copy the code
The enumeration
Digital enumeration
Numeric enumeration without specifying an initial value, enumeration values are incremented from 0. If a member is initialized to 1, subsequent members are incremented from 1.
String enumeration
Each member of the enumeration must be initialized with a string.
Heterogeneous enumeration
Enumeration members, numeric members, and string members mixed. But it’s best not to use it that way.
Enumerated type
When all enumerators have literal enumeration values, enumerations take on special semantics and can become a type.
enum Witcher {
Ciri,
Geralt
}
// ok
let witcher: Witcher = Witcher.Ciri
// error
let witcher: Witcher = 'V'
Copy the code
Runtime enumeration
At run time, an enumeration is a real object that can be used as an object
enum Witcher {
Ciri = 'Queen',
Geralt = 'Geralt of Rivia'
}
function getGeraltMessage(arg: {[key: string] :string}) :string {
return arg.Geralt
}
getGeraltMessage(Witcher) // Geralt of Rivia
Copy the code
Compile-time enumeration
At run time, though, an enumeration is a real object. But using keyof doesn’t behave like a normal object. You must use keyof Typeof to get all attribute names for the enumeration.
👻 : This is not available in typescript Chinese
enum Witcher {
Ciri = 'Queen',
Geralt = 'Geralt of Rivia'
}
type keys = keyof Witcher / / the toString, charAt...........................
type keys = keyof typeof Witcher // Ciri, Geralt, all enumerated types
Copy the code
Const enumeration
Const enumerations are removed during TS compilation to avoid additional performance overhead.
const enum Witcher {
Ciri = 'Queen',
Geralt = 'Geralt of Rivia'
}
const witchers: Witcher[] = [Witcher.Ciri, Witcher.Geralt]
/ / the compiled
// const witchers = ['Queen', 'Geralt of Rivia']
Copy the code
Enumerated static methods and properties
A namespace with the same name and an enumeration type will undergo a “declaration merge”, and the functions and variables of the namespace export will become static methods of the enumeration, static properties.
enum Color { Blue, Yellow, Green }
namespace Color {
export function print(color: Color) :void {
alert(Color[color])
}
export const name = 'colors'
}
Color.print(Color.Blue) // Blue
alert(Color.name) // colors
Copy the code
Open enumeration
Enumerations with the same name will be merged, but you need to specify an initial value for the second enumeration with the same name.
enum Color { Red, Green, Blue }
enum Color { Yellow = 3, Black }
Copy the code
Type inference
// x is automatically inferred as number
let x = 2
/ / zoo is automatic inference as (Rhino | Elephant | Snake) [] type
// If Rhino, Elephant, and Snake have the same supertype of Animal, zoo will be inferred to be Animal[]
let zoo = [new Rhino(), new Elephant(), new Snake()];
Copy the code
Context type
Typescript extrapolates the type of the function on the right from the type of window.onmousedown.
👻 : This is inconsistent in typescript’s Chinese language.
// typescript can infer that mouseEvent is of type mouseEvent
window.onmousedown = function(mouseEvent) {
// ok
console.log(mouseEvent.button)
// error
console.log(mouseEvent.kangaroo)
}
window.onscroll = function(uiEvent) {
// Error, uiEvent is automatically inferred to be of the uiEvent type. The uiEvent type does not contain the button attribute
console.log(uiEvent.button)
}
Copy the code
noImplicitAny
Enable the compiler option noImplicitAny. The compiler will warn when type inference can only be inferred as any
Type compatibility
Typescript type compatibility is structure-based, not notional. The following code is perfectly fine in TS, but throws errors in name-based languages such as Java.
interface Named { name: string }
class Person {
name: string
}
let p: Named
// ok
p = new Person()
// We can use const assertions directly.
let c = 'python' as const
c = 'Swift' // error
Copy the code
Covariant, Contravariant, Bivariant
- Covariant parent = subclass 👌; Subclass = parent class 🙅
- Contravariant parent = subclass 🙅; Subclass = parent class 👌
- Bivariant parent = subclass 👌; Subclass = parent class 👌
Object Compatibility Covariant
interface Foo {
name: string;
}
interface Bar extends Foo {
age: number;
}
letx! : Foo;lety! : Bar; x = y// ok
y = x // error
Copy the code
✨ Function Compatibility
Function parameter type compatibility Bivariant Contravariant
Compatibility of function parameters, with bidirectional covariance
/ / animals
interface Animal {
name: string
}
/ / the cat
interface Cat extends Animal {
color: string
}
// American bobtail cat
interface USAShorthair extends Cat {
age: number
}
type handle = (it: Cat) = > void
// Ok, it is safe
const handle1: handle = (it: Animal) = > {}
// Ok, not safe, but allowed
// Since the type handle argument is Cat, it is allowed to accept other Cat subtypes
// If it is USAShorthair, other subclasses of Cat will be rejected, so it is not secure
const handle2: handle = (it: USAShorthair) = > {}
Copy the code
Disable function parameter bidirectional covariant Contravariant
Enabling strictFunctionTypes disables bidirectional covariant of function parameters. The parameter type is contravariant
type handle = (it: Cat) = > void
// ok
const handle1: handle = (it: Animal) = > {}
// error
const handle2: handle = (it: USAShorthair) = > {}
Copy the code
Function return value type Compatibility (Covariant)
If the function returns an object, the compatibility is the same as the object
interface Foo {
name: string;
}
interface Bar extends Foo {
age: number;
}
// Bar is a subclass of Foo, and return value type compatibility is covariant
// Foo (parent) = Bar (subclass) ok
let x: (a)= > Foo;
let y: (a)= > Bar;
x = y; // ok
y = x; // error
Copy the code
Quantitative compatibility of function arguments
A function that allows you to ignore some arguments (contravariant?)
let x: (a: number) = > void = (a) = > {} // The number of parameters is small
let y: (a: number, b: string) = > void = (a, b) = > {} // The number of parameters is large
y = x; // ok
x = y; // error
Copy the code
Enumeration compatibility
Enumeration types are compatible with numeric types; different enumeration types are incompatible
enum Bar { T, R }
enum Foo { T, R }
// ok
const a: number = Bar.T
// error
const b: Foo = Bar.T
Copy the code
Class compatibility
The compatibility of class types is similar to that of object literals. But class types only compare instance parts of classes; static members and constructors fall outside the scope of compatibility comparisons.
class Bar {
constructor(public name: string) {}}class Foo {
constructor(public name: string.public age: number) {}}class Faz {
constructor(public age: number) {}}class Boo {
static version: number
constructor(public name: string) {}}letbar! :Barletfoo! :Fooletfaz! :Fazletboo! :Boo foo = bar// error, missing age instance attribute, compatibility rules are similar to objects
bar = foo // ok
bar = faz // Error, name and age are incompatible
boo = bar // ok, static members do not compare compatibility
Copy the code
Generic compatibility
When generics are compared for compatibility, you need to specify generic parameters for comparison. When no generic parameter is specified, the default type is any.
type Bar<T> = {
name: T
}
type Foo<U> = {
name: U
}
type Far<R> = {
name: R
}
leta! :Bar<string>letb! :Foo<string>letc! :Far<number> a = b// ok
b = c // error
Copy the code
Compatibility Summary
- Object Compatibility Covariant
- Function compatibility
- Function parameter compatibility Bivariant – safer -> Contravariant
- Function return value Compatibility (Covariant)
- Function parameter number Contravariant
- Enumeration compatibility (number <= enumeration OK, enumeration A <= enumeration B error)
- Class compatibility (similar to object compatibility type)
- Generic compatibility (need to specify generic parameters after comparison)
Nominal type
Ts compatibility is structure-based. But sometimes we do want to distinguish between two types that have the same structure.
Use literal types
Use literal types to distinguish types of the same structure
interface People<T extends string> {
name: string,
age: number,
color: T
}
letblackPeople! : People<'black'>
letwhitePeople! : People<'white'>
// Same structure, but incompatible types
// The example does not involve any racist ideas
blackPeople = whitePeople // error
whitePeople = blackPeople // error
Copy the code
Use enumerated
interface Foo { name: string }
enum FooEnum {}
interface Boo { name: string }
enum BooEnum {}
type FooTitular = Foo & FooEnum
type BooTitular = Boo & BooEnum
let a:FooTitular = { name: '123' } as FooTitular
let b:BooTitular = { name: '456' } as BooTitular
// Same structure but not compatible
a = b // error
b = a // error
Copy the code
Using an interface
Breaking structural compatibility by adding an unrelated attribute to a type, the naming convention for unrelated attributes usually begins with _ and ends with Brand
interface A extends String {
_aBrand: number;
}
interface B extends String {
_bBrand: number;
}
let a: A = 'a' as any;
let b: B = 'b' as any;
// Same structure, but incompatible type
a = b; // error
b = a; // error
Copy the code
✨ Advanced type
Cross type
Stacking multiple types into one type
function assign<T extends object.U extends object> (x: T, y: U) :T & U {
return{... x, ... y } }class Foo {
constructor(public foo:string) {}}class Bar {
constructor(public bar: string) {}}const boo = assign(new Foo('foo'), new Bar('bar')) // Boo is a crossover type of Bar & Foo
boo.bar // ok
boo.foo // ok
Copy the code
The joint type
Union types indicate that a value can be one of several types. When a value is a union type, we can be sure that it contains common members of multiple types.
leta! :Boo | Foo | Baz a.sex// Error, when a is Boo, Baz, there is no sex attribute
(a as Foo).sex // ok
Copy the code
Type of protection
Custom type protection
Define a special function that returns a predicate of type arg is type. Arg is an argument in a custom type protection function.
interface Boo { name: string }
interface Foo { sex: boolean }
interface Baz { age: number }
type BooFooBaz = Boo | Foo | Baz
leta! : Boo | Foo | Bazfunction isBoo(arg: BooFooBaz) :arg is Boo {
return (arg asBoo).name ! = ='undefined'
}
alert(a.name) // error
if (isBoo(a)) {
alert(a.name) // ok
}
Copy the code
Typeof type protection
Provides type protection for number, String, Boolean, and symbol. We can do without abstracting typeof into a special function that returns a type predicate.
leta! :number | string
if (typeof a === 'string') {
alert(a.length) // ok
}
if (typeof a === 'number') {
alert(a - 1) // ok
}
Copy the code
Instanceof protection
Provides type protection for class types
class Foo {
getFoo(): void{}}class Bar {
getBar(): void{}}leta! : Bar | Fooif (a instanceof Foo) {
a.getFoo() // ok
a.getBar() // error
}
if (a instanceof Bar) {
a.getBar() // ok
a.getFoo() // error
}
Copy the code
In type protection
interface A { x: number }
interface B { y: string }
function doStuff(q: A | B) {
// Determine if there is an x attribute
if ('x' in q) {
alert(q.x)
} else {
alert(q.y)
}
}
Copy the code
Literal-type protection
interface Foo {
name: string
type: 'foo'
}
interface Bar {
name: string
type: 'bar'
}
function temp (arg: Foo | Bar) {
if (arg.type === 'foo') {
/ / the type Foo
} else {
/ / Bar type}}Copy the code
Null type
Null, undefined can be assigned to any type. Adding strictNullChecks to tsconfig.js prevents this behavior. After strictNullChecks is enabled, a variable does not automatically contain null and undefined by default.
// start strictNullChecks
let a: string = '123'
a = null // ok
StrictNullChecks is enabled
let b: string = '123'
b = null // error
Copy the code
Assertion excludes undefined, null type
Add! After the variable name. You can assert that undefined and null types are excluded
let a: string | null | undefined
a.length // errora! .length// ok
Copy the code
✨ Type alias
type Foo = string
type Bar = {
name: string,
age: number
}
Type aliases can also be used to declare functions
type Func = {
(a: number) :number;
(a: string) :string;
}
// Type aliases can also be generic
type Boo<T> = {
key: T
}
// The type alias can refer to itself
type LinkListNode<T> = {
value: T,
next: LinkListNode<T> | null,
prev: LinkListNode<T> | null
}
Copy the code
The difference between type aliases and interfaces
- A type alias can introduce a name for any type. Examples are basic types, union types, and so on
- Type aliases do not support inheritance
- Type aliases do not create a real name
- Type aliases cannot be implemented, whereas interfaces can be implemented by derived classes
- The compiler throws an error when a type alias is the same name, and merges when an interface is the same name
String literal type
leta! :'js'
a = 'js' // ok
a = 'java' // error
letb! :'c#' | 'c++'
b = 'c#' // ok
b = '.net' // error
Copy the code
Numeric literal type
letx! :1
x = 2 // error
lety! :1 | 2
y = 2 // ok
y = 3 // error
Copy the code
The index type
Index type query operator
The result of keyof T is a union of public property names on T
type Keys<T> = keyof T
interface Witcher {
name: string,
age: number
}
// Witcher public attribute name union
let keys: Keys<Witcher>; // name | age
Copy the code
type Keys<T> = keyof T
type Witchers = [string.string]
let keys: Keys<Witchers>; / / '0' | '1' | length | toString...
type Keys<T> = keyof T
type Witchers = Array<string>
let keys: Keys<Witchers>; / / number | length | toString...
Copy the code
function pluck<T.K extends keyof T> (o: T, names: K[]) :T[K] []{
return names.map(n= > o[n])
}
const witcher = {
name: geralt,
age: 88
}
// (string | number)[]
// [' geralt ', '88']
const values = pluck(witcher, ['name'.'age'])
Copy the code
Index access operator
T[K], index access operator. But you need to make sure that K extends keyof T
function getValue<T.K extends keyof T> (obj: T, key: K) :T[K] {
return obj[key]
}
Copy the code
Mapping type
Create a new type from an old type, a mapping type
// Read-only mapping type
type CustomReadonly<T> = {
readonly [K in keyof T]: T[K]
}
// Optional mapping type
type CustomPartial<T> = {
[K inkeyof T]? : T[K] }// The mapping type can be null or undefined
type NullUndefinedable<T> = {
[K in keyof T]: T[K] | null | undefined
}
Copy the code
type CustomPick<T, K extends keyof T> = {
[P in K]: T[P];
}
interface Bar {
name: string,
age: number,
sex: boolean
}
// Extract the type of the specified key
// Foo = { name: string, age: number }
type Foo = CustomPick<Bar, 'name' | 'age'>
Copy the code
type CustomRecord<K extends string, T> = {
[P in K]: T;
}
// { name: string, title: string, message: string }
type Bar = CustomRecord<'name' | 'title' | 'message'.string>
Copy the code
Built-in mapping type
Exclude
Exclude
, extracts from T and cannot be assigned to U
// string | boolean
/ / string | Boolean cannot be assigned to the type number
type Foo = Exclude<string | number | boolean.number>
Copy the code
Extract
Extract
Extract the type that can be assigned to U from T
// number
// Only number can be assigned to number
type Foo = Extract<string | number | boolean.number>;
Copy the code
NonNullable
NonNullable
, remove null and undefined from T
type Bar = string | null | undefined
// Foo
type Foo = NonNullable<Bar>;
leta! : Foo;// error
a = null
Copy the code
Nullable
removes null and undefined from attributes based on NonNullable
encapsulation
type NonNullUndefinedable<T> = {
[K in keyof T]: NonNullable<T[K]>
}
interface Bar {
name: string | null | undefined
age: number | null | undefined
sex: boolean | null | undefined
}
// Foo { name: string, age: number, sex: boolean }
type Foo = NonNullUndefinedable<Bar>
Copy the code
ReturnType
ReturnType
, gets the return value type of T
type BarCall = (a)= > number
// number
type BarReturn = ReturnType<BarCall>
Copy the code
InstanceType
InstanceType
, get the InstanceType of T
class Bar {}
// Bar, the instance type of the constructor of Bar, is Bar type
type Foo = InstanceType<typeof Bar>
Copy the code
Omit
Omit Omit has become a built-in type in TS3.5 version (see below)
// Exclude
Exclude all K types from keyof T
// Pick
maps new types with only U attributes
,>
// Analog Omit mapping type
type CustomOmit<T, K> = Pick<T, Exclude<keyof T, K>>
Copy the code
lib.d.ts
When you install typescript, you install a set of declaration files lib.dom.d.ts, lib.es2015.core.d.ts, and so on. They contain various common environment declarations that exist in the javaScript runtime and dom
// Lib.dom.d. ts Content excerpt
/** A window containing a DOM document; the document property points to the DOM document loaded in that window. */
interface Window extends EventTarget, WindowTimers, WindowSessionStorage, WindowLocalStorage, WindowConsole, GlobalEventHandlers, IDBEnvironment, WindowBase64, AnimationFrameProvider, WindowOrWorkerGlobalScope, WindowEventHandlers {
readonly applicationCache: ApplicationCache;
readonly caches: CacheStorage;
readonly clientInformation: Navigator;
readonly closed: boolean;
readonly crypto: Crypto;
customElements: CustomElementRegistry;
defaultStatus: string;
readonly devicePixelRatio: number;
readonly doNotTrack: string;
readonly document: Document;
/ /... more
}
Copy the code
Modify primitive type
Create globals.d.ts global declaration file and modify the original type in the global declaration file
// globals.d.ts
// Modify the Window interface and add the HelloWorld method
interface Window {
helloworld(): 'helloworld'
}
// Change the static member of Date and add the today method
interface DateConstructor {
today(): Date
}
/ / use
window.helloworld()
Date.today()
Copy the code
Type of flow
Copy type
Import works with namespace or Module to replicate types.
class Foo {}
const Bar = Foo // Bar is a reference to Foo, not a type
let bar: Bar // error, there is no Bar type
Copy the code
namespace classs {
export class Foo {}
}
import Bar = classs.Foo // Copy types can be implemented
let bar: Bar // ok
Copy the code
Gets the type of the variable
Use let and typeof to get the typeof the variable itself (if const, typeof gets the literal type)
let foo = 'foo'
letbar! :typeof foo
bar = 'bar' // ok, bar is a string
bar = 123 // error
let foo = [1.2.3]
letbar! :typeof foo
bar = [4.5.6] // ok
bar = ['4'.'5'.'6'] // error, bar is of type number[]
Copy the code
Gets the type of a class (instance) member
class Foo {
constructor (public name: string) {}}declare let _foo:Foo
// bar is a string, and _foo is an instance of Foo
let bar: typeof _foo.name
Copy the code
Gets the literal type
Literal types can be obtained using the const keyword and typeof
const foo = 'foo'
letbar! :typeof foo
bar = 'bar' // error
bar = 'foo' // ok, foo is of type foo
Copy the code
The namespace
Use namespaces to organize code to avoid naming conflicts. To access the contents inside the namespace, use export to export the contents
namespace Lib {
export namespace Math {
export function sum(a: number, b: number) :number {
return a + b
}
}
}
Lib.Math.sum(1.2)
Copy the code
Namespace alias
Import alias = namespace.namespace. You can create an alias for the namespace
namespace Lib {
export namespace Math {
export namespace Geometry {
export function pythagoreanTheorem(x: number, y: number) :number {
return x * x + y * y
}
}
}
}
// When used, there are multiple levels of relationships
Lib.Math.Geometry.pythagoreanTheorem(1.2)
// Use the namespace alias
import Geometry = Lib.Math.Geometry
Geometry.pythagoreanTheorem(1.2)
Copy the code
Use other javascript libraries
Ts requires the type declaration file xx.d.ts when using a class library written in JS. Most libraries usually expose a top-level object that can be represented in a namespace. Let me give you an example.
// node_modules/custom-lib/index.d.ts
export declare namespace Lib {
export namespace Geometry {
export function pythagoreanTheorem(x: number, y: number) :number {
return x * x + y * y
}
}
}
}
export declare const gravitationalConstant: number
Copy the code
// src/main.ts
import { Lib, gravitationalConstant } from 'custom-lib'
Lib.Geometry.pythagoreanTheorem(gravitationalConstant, 2)
Copy the code
✨ something not mentioned in the manual guide
New syntax for read-only arrays
// The original syntax
const bar: ReadonlyArray<string> = ['Hello'.'World']
// New syntax
const boo: readonly string[] = ['Hello'.'World']
Copy the code
Const type assertion
With const assertions, typescript adds its own literal type to variables. Let me give you an example
// ts automatically deduces the type 'string' for x
// let x: string = '123'
let x = '123'
// x can be reassigned
x = '456'
// Using const assertion adds its own literal type to x
// let x: '123' = '123'
let x = '123' as const
// error, x cannot be reassigned
x = '456'
Copy the code
When using const assertion:
- Property of the object literal
readonly
Becomes a read-only property - The array literal becomes
readonly tuple
Read-only tuples - Literal types cannot be extended (such as from
hello
Type tostring
Type)
// type '"hello"'
let x = "hello" as const
// type 'readonly [10, 20]'
let y = [10.20] as const
// type '{ readonly text: "hello" }'
let z = { text: "hello" } as const
Copy the code
Const assertions support two syntax, as syntax, and the
syntax can be used in non-TSX files
// type '"hello"'
let x = <const>"hello"
// type 'readonly [10, 20]'
let y = <const> [10.20]
// type '{ readonly text: "hello" }'
let z = <const>{ text: "hello" }
Copy the code
Unknown type
The unknown type is similar to any type. Unlike any type. An unknown type can be assigned to any type, but it must be asserted before being assigned to any other type.
Unknown is useful for apis that want to represent any value, but must perform type checking before using it.
let x:any = 'Hello'
// ok
let y:number = x
let x: unknown = 'Hello'
// error
let y: number = x
// ok, must be asserted to assign to the confirmed type
let z: number = x as number
Copy the code
✨ ✨ ✨ infer
At first, it was hard for me to understand what Infer actually does. Let’s start with two simple examples
type Pro<T> = T extends Promise<infer R> ? Promise<R> : T
type Bar = Pro<Promise<string[] > >// Bar = Promise
, Promise
satisfies the Promise
constraint
[]>
[]>
type Boo = Pro<number> // Boo = number
Copy the code
Type Pro, which checks for the generic parameter T, returns the type of the Promise
if it satisfies the Promise constraint, and returns type T otherwise
type Param<T> = T extends (param: infer P) => any ? P : T
type getUser = (id: number) = > object
type Foo = Param<getUser> // Foo equals the parameter type of getUser, number type
type Bar = Param<boolean> // Bar equals Boolean
Copy the code
Type Param, checks the generic parameter T and returns the type of the parameter if it satisfies the constraint (Param: infer P) => any; otherwise, type T is returned
Infer mapping type
The Infer mapping type is built into typescript
type ReturnType
Extracts the type of the return value of the get function
type Foo = ReturnType<(a)= > string[] >// Foo = string[]
Copy the code
Type ReturnType internal implementation
// Customize ReturnType mapping type
type CustomReturnType<T> = T extends(... arg:any[]) => infer P ? P : T
type Foo = CustomReturnType<(a)= > string[] >// Foo = string[]
Copy the code
type ConstructorParameters
Extract the parameter type of the constructor
class Foo {
constructor (public name: string.public age: number) {}}type Params = ConstructorParameters<typeof Foo> // [string, number]
Copy the code
Type ConstructorParameters internal concrete implementation
// ConstructorParameters map type
type CustomConstructorParameters<T extends new(... args:any[]) = >any> = T extends new(... args: infer P) =>any ? P : never
type Params = CustomConstructorParameters<typeof Foo> // [string, number]
Copy the code
Tuple type -> union type
How will/string, number into a string | number?
type CustomTuple = [string.number.boolean]
type TupleToUnion<T> = T extends Array<infer P> ? P : never
type CustomUnion = TupleToUnion<CustomTuple> // string | number | boolean
Copy the code
Union type -> Cross type
The examples in TypeScript In Plain English have some problems in the current version (3.6.4).
type Omit
Omit Omit is a new type of mapping in version 3.5 that removes all types of K from the T.
interface Foo {
name: string
age: number
}
// Bar = { name: string }
type Bar = Omit<Foo, 'age'>
Copy the code
In previous versions type Omit
= Pick
> can be implemented
reference
Thanks big guys pay ❤️
- ✨ Sanmao’s blog
- Understand TypeScript in depth
- TypeScript Chinese website
- TypeScript
- TypeScript Tutorial – ‘infer’ keyword
- TypeScript union to tuple
- What are covariance and contravariance?
- Differences Between TypeScript Type vs Interface