TypeScript Getting started notes
This article is a summary of some TS basics and an interpretation of the official built-in tools Pick, Partial, diff
Build a typescript environment
Install typescript globally and initialize tsconfig.js
NPM install -g typescript NPM init // generates ts.config.js file TSC --initCopy the code
Initial Configuration
Configure some common NPM commands in package.json as well as some COMPILATION options for TS
-
Configure script in package.json
{ "main": "src/index.ts"."scripts": { "build": "tsc"./ / compile "build:w": "tsc -w" // Listen to the file, if there are changes, then compile}}Copy the code
-
Tsconfig. Json configuration
"compilerOptions": {
"target": "es5".// Specify ECMAScript target version: 'ES5'
"module": "commonjs".// Specify the module to use: 'commonJS ',' AMD ', 'system', 'umd' or 'es2015'
"moduleResolution": "node".// Select the module resolution policy
"experimentalDecorators": true.// Enable experimental ES decorator
"allowSyntheticDefaultImports": true.// Allow default imports from modules that do not have default exports set.
"sourceMap": true.// When compiling ts files into js files, generate the corresponding map file
"strict": true.// Enable all strict type checking options
"noImplicitAny": true.// There is an error with an implied any type on expressions and declarations
"alwaysStrict": true.// Check modules in strict mode and add 'use strict' to each file
"declaration": true.// Generate the corresponding.d.ts file
"removeComments": true.// Delete all comments after compilation
"noImplicitReturns": true.// Not all return paths of the function have return value error
"importHelpers": true.// Import helper functions from tslib
"lib": ["es6"."dom"].// Specify the library file to be included in the build
"typeRoots": ["node_modules/@types"]."outDir": "./dist"."rootDir": "./src"
},
"include": [ // ts files to compile a * indicates file matching ** indicates ignoring file depth issues
"./src/**/*.ts"]."exclude": [
"node_modules"."dist"."**/*.test.ts"]},Copy the code
TypeScript basics
Types in TS (any,unknown,never,enum)
-
any
Specify a type for variables whose type is not known at programming time. These values may come from dynamic content, such as user input or third-party code libraries. Using any allows them to pass compile-time checks directly
-
unknown
The main difference between Unknown and any is that the unknown type is more rigorous: some kind of check must be performed before most operations are performed on values of unknown type, whereas no check is required before operations are performed on values of any type
Here’s the difference between unknown and any. First, they can both be of any type:
any
let value: any; value = true; // OK value = 1; // OK value = "Hello World"; // OK Copy the code
unknown
let value: unknown; value = true; // OK value = 1; // OK Copy the code
See what the difference is:
let value: any;
value.foo.bar; // OK
value(); // OK
new value(); // OK
Copy the code
If the type is unknown
let value: unknown;
value.foo.bar; // ERROR
value(); // ERROR
new value(); // ERROR
Copy the code
As you can see, although they can be of any type, unknown types cannot be instantiated, getters, function execution, etc., until they are determined to be of a type.
Any is ok. That’s why unknown is a safer any
-
never
The never type represents the types of values that never exist. The never type is a subtype of any type and can be assigned to any type; However, no type is a subtype of never or can be assigned to a type of never (except never itself).
Function error(message: string): never {throw new error(message); } // Empty array, and always empty const empty: never[] = []Copy the code
-
object
Object represents a non-primitive type, that is, a type other than number, string, Boolean, symbol, NULL, or undefined.
enum Direction { Center = 1 } let value: object value = Direction // OK value = [1] // OK value = [1.'hello'] // OK value = {} // OK Copy the code
-
enum
An enumerated type is a type that many languages have that declares a set of named constants and can be defined as an enumerated type when a variable has several possible values.
// Enumeration of numbers
// When an enumerated type is declared, its value is the default numeric type, although no value is assigned to it, and it is accumulated from 0 by default
enum Direction {
Up,
Down,
Left,
Right
}
console.log(Direction.Up === 0); // true
console.log(Direction.Down === 1); // true
// String enumeration
enum Direction {
Up = 'Up',
Down = 'Down',}Copy the code
Enumerations have the ability to map backwards, so take a look at what enumerations look like when compiled into JavaScript:
// Reverse mapping enum Direction {Up, Down, Left, Right} console.log(Direction[0]); Var Direction; (function (Direction) { Direction[Direction["Up"] = 10] = "Up"; Direction[Direction["Down"] = 11] = "Down"; Direction[Direction["Left"] = 12] = "Left"; Direction[Direction["Right"] = 13] = "Right"; })(Direction || (Direction = {}));Copy the code
You can think of an enumerated type as a JavaScript object, but because of its special construction, it has the property of simultaneous forward and backward mapping
Generic constraints, indexable types
-
Generic constraint
You can constrain generics with
, which is one of number or string. An error is reported when a Boolean type is passed
type Params = nubmer | string function test<t extends Params>(params:T): T { return T } Copy the code
-
The index sign
Index signature includes numeric index and string index. Its syntax is as follows:
[prop]:string :any
|[prop]:number :any
// The interface declares that the index of the object can be any string, Inteface Obj {name:string, [prop]:string :any} let Obj :Obj ={name:'123', age:2323,}Copy the code
Type derivation, type assertion, assignment assertion, type guard,
-
Type inference
In TypeScript, type inference helps provide types where they are not explicitly specified. Here’s an example
// there is no declaration of type x,ts is automatically derived to type number let x = 3; x = '123' // Error: 'string' cannot be assigned to 'number' // The result is const bar = [1.2]; let [a, b] = bar; a = 'hello'; // Error: 'string' cannot be assigned to 'number' Copy the code
-
Type Assertion (AS)
In cases where TS does not correctly or accurately infer the type, unnecessary warnings or errors may be generated.
Such as:
const person = {}; person.name = 'zhangsan'; // Error: 'name' attribute does not exist in '{}' person.age = 20; // Error: 'age' does not exist in '{}' // Type assertion interface Person {name: string; age: number; } const person = {} as Person;Copy the code
Because of type inference, the type of Person is {} and there are no additional attributes, so the developer needs to use AS to determine what type the variable is
3. Assignment assertion (!)
Explicit assignment assertion is a feature that allows you to put! After the instance property and variable declaration to indicate that the property is determined to have been assigned:
let x: number; initialize(); console.log(x + x); // The variable "x" is used before the assignment. function initialize() { x = 10; } Copy the code
Use! To indicate that the property is certain that it has been assigned
let x: number; initialize(); console.log(x! + x!) ; //ok function initialize() { x = 10; }Copy the code
4. Type guard
In fact, type guard means to narrow the scope of the type, let’s see a few examples
class Person { name = 'xiaomuzhu'; age = 20; } class Animal { name = 'petty'; color = 'pink'; } // Type refinement via instanceof, in statements function getSometing(arg: Person | Animal) { // The type is refined to Person if (arg instanceof Person) { } // The type is refined to Person if (arg instanceof Animal) { } if ('age' in arg) {} } Copy the code
Cross type, union type, type alias, identifiable union type
1. Cross types
Interface IAnyObject {[prop: string]: {' T & U ': {' T & U' : {' T & U ': { any } function mixin<T extends IAnyObject, U extends IAnyObject>(first: T, second: U): T & U { const result = <T & U>{}; for (let id in first) { (<T>result)[id] = first[id]; } for (let id in second) { if (! result.hasOwnProperty(id)) { (<U>result)[id] = second[id]; } } return result; } const x = mixin({ a: 'hello' }, { b: 42 }); // now x has a const a = x.a; const b = x.b; ` ` `Copy the code
-
The joint type
Typeof type protection is used to distinguish between types
// The argument may be passed in two types. Use if to narrow down the type range function formatCommandline(command: string[] | string) { let line = ' '; if (typeof command === 'string') { line = command.trim(); } else { line = command.join(' ').trim(); }}Copy the code
-
Type the alias
Type aliases give a type a new name. Type aliases sometimes look like interfaces, but can work with primitive values, union types, tuples, and any other type you need to write by hand.
type some = boolean | string const b: some = true // ok const c: some = 'hello' // ok const d: some = 123 // Cannot assign type '123' to type 'some' // Reference yourself type Tree<T> = { value: T; left: Tree<T>; right: Tree<T>; } Copy the code
A type alias looks a lot like an interface, so how do you distinguish between the two? Interface can only be used to define object types, while type declaration can also define cross, union, primitive types, etc. Type declaration is obviously more widely applicable.
But interfaces also have specific uses:
- The interface mode implements the extends and implements of interfaces
- Interface can implement interface merge declaration
-
Identifiable union types
Suppose a scenario, now two functions, one is to add users, namely add, and one is to remove users, namely remove.
- Has common singleton type properties-recognizable characteristics,(in the following example, active has unique string literals)
- A type alias contains union types
- Features of type guards (e.g., if switch must be used to determine which type scope opt.action belongs to, i.e. delete and create)
Example:
inteface conf1 { action:'add'.payload:'xiaomuzhu' } inteface conf2 { action:'remove'.payload:'xiaomuzhu' } type params = conf1 | conf2 function fetch(opt:params){ switch (opt.action) { case 'add': console.log(opt.payload); break; default: break; }}Copy the code
TypeScript progression
The advanced content in typeScript refers to a set of advanced types, including exposure to the usage and implementation of some of ts’s typing tools
pick
Looking at a scenario, we now need a pick function that pulls the specified properties from the object
In JavaScript this function would look like this:
function pick(o, names) {
return names.map(n= > o[n]);
}
// Use of the pick method
pick({name:'a'.age:'88'},'name'.'age'])
Copy the code
How do you implement these value functions in TypeScript? Maybe
interface Obj {
[key: string]: any
}
function pick(o: Obj, names: string[]) {
return names.map(n => o[n]);
}
Copy the code
It seems easy to write, but you’ll find that the type definition is not rigorous enough:
- The members of the names argument should be attributes of the o argument, so instead of a broad definition like string, it should be more accurate
- The return type of the pick function is any[], which can be more precise. The return type of the pick function should be the combination of the attribute value types
How do you define a type more precisely? There are two type operators that you must understand: the index type query operator and the index access operator.
The index type
-
Index Type query operator (KEYof)
Keyof is the index type query operator. You can use keyof on the generic T to get all the public attribute names on the generic T to form the joint type. For example, we have an Images class that contains both SRC and Alt public attributes and uses keyof to name the attributes:
class Images { public src: string = 'https://www.google.com.hk/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png' public alt: string = 'Google' public width: number = 500 } type propsNames = keyof Images // Type propsNames looks like this //propsNames = src | alt | width Copy the code
-
Index access operator
Keyof can be used to query the attribute name of an index type. How to obtain the attribute value type corresponding to the attribute name? This is where index accessors come in. Similar to JavaScript types that access property values, operators of access types are accessed through [], that is, T[K].
Class Images' {public SRC: string = 'https://www.google.com.hk' public Alt: string = 'Google' public width: Number = 500} type propsNames = keyof Images Make sure that propsType must be the joint type of all keys under Images. Type propsType = Images[propsNames]Copy the code
The type of the corresponding attribute value can be obtained through the type accessor T[K]. In keyof limiting K to the joint type of T generic key, the retrieved data T[K][] is exactly the type of the returned value
The final implementation of pick function is as follows:
function pick<T, K extends keyof T>(o: T, names: K[]): T[K][] { return names.map(n => o[n]); } const res = pick(user, ['token', 'id', ]) Copy the code
Partial implementation
For example, if you have a User interface, there is a requirement to make all members of the User interface optional. What should you do? Shall I add them one by one? Is there a more convenient way? Ts provides the ability to map a type to a new type, let’s say a mapping type, its syntax is [K in Keys],
interface User { username: string id: number token: string avatar: string role: // Optional type partial<T> = {[K in keyof T]? : T[K]} // Read-only type readonly<T> = {readonly[K in keyof T]: T[K]}Copy the code
Partial interpretation
- K
A type variable, bound in turn to each property, corresponds to the type of each property name
- Keys
A combined type of string literals representing a set of attribute names.
Implementation steps:
- We first find Keys, the union type of string literals. Assuming that the type passed in is generic T, we get keyof T, the union type of the attribute name of the type passed in.
- Then we need to map the property names of keyof T one by one [K in keyof T],
- If you want to change all attribute members to optional types, you need T[K] to fetch the corresponding attribute value
- Finally, regenerate an optional new type {[K in keyof T]? : T [K]}.
Conditions in the
Sometimes you don’t have to write code to determine the type, but what type does it have to be
If T can be assigned to U, then the type is X. If T can be assigned to U, then the type is Y. F = T extends U? X : YCopy the code
Using the type tool Diff<T, U> as an example, find out what part of the T type U does not contain
B / / joint type "a" | "" | |" c "" d" and "a" | "c" | "f", which does not include the "b" | "d" type R = Diff < "a" | "b" | | "c" "d", "a" | "c" | "f" >; / / "b" | "d" / / similar to js array filter type, filter extends < T, U > = T U? T : never; type R1 = Filter<string | number | (() => void), Function>; / / rejecting null, and undefined type NonNullable < T > = Diff < T, null | undefined >;Copy the code
Think about it, I have an interfacePart, and now I need to write a utility type to pull out the name of the function type in interface
```js interface Part { id: number; name: string; subparts: Part[]; updatePart(newName: string): void; } type R = FunctionPropertyNames<Part>; // type R = "updatePart" ```Copy the code
In TypeScript type programming, we iterate over the interface, retrieve the Function, and retrieve the key. We iterate over the interface, retrieve the Function and retrieve the key.
type FunctionPropertyNames<T> = { [K in keyof T]: T[K] extends Function ? K : never }[keyof T]
Copy the code
Take apart the implementation of the above code in turn
- Assuming that Part is substituted into the generic T,[K in keyof T] is equivalent to traversing the entire interface
- K is the key of the interface, and T[K] is the value of the interface
- Next, verify the type of value with the conditional type, keeping value as the key of the new interface if it is Function, otherwise never
- Here we get the new interface after traversal:
- [keyof T] is used as the keyof the old interface Part
- But since the value of ID name and subparts is never, no type is returned, so only ‘updatePart’ is returned.
The never type means that there will be no value, that is, nothing, okay