Write in front
The difficulty of this paper is medium to low. Most of the points involved are how to reasonably apply ts in the project, and a small part will involve some principles. It has a wide audience. It can be eaten with or without TS foundation
After reading this article, you may gain:
1. If you are not familiar with TS, this article can help you complete the study of ts application, accompanied by many Demo examples to guide business applications.
2. If you are familiar with TS, this article can be used as a review article to review your knowledge, hoping to trigger your new discovery and thinking at some points.
3. For IState and IProps of class components, compare some writing methods and thinking of Hook components.
TIPS: super easy to use online TS editor (many configuration items can be configured manually)
Portal: https://www.typescriptlang.org/
What is TS
In general, TypeScript is a superset of JavaScript. It has optional types and can be compiled into pure JavaScript. (I have always regarded TypeScript as the Lint of JavaScript)
So the question is, why should TS be designed to be static? Or in other words, why do we need to add type specifications to JavaScript?
Classic question and answer session - because it can solve some unresolved pain points of JS: 1. JS is a dynamically typed language, which also means that we don't know the type of variables before instantiation, but using TS can avoid classic low-level errors before running.
Example: Uncaught TypeError: 'xxx' is not a function ⚠️ Error in dictionary level:



JS is like this. It tells me that there is an error only when an error occurs at runtime, but when TS intervenes:

good heavens! Throw the problem directly in the editor stage, nice!
2. Lazy Carnival! The specification is convenient and not error prone. For VS Code, the most it can do is to indicate whether this attribute exists or not, but it can not accurately indicate what type this attribute is, but TS can deduce / reverse deduce through type (in other words, if you do not explicitly write a type, you will use type inference to infer the type you are using), This perfectly optimizes the code completion:

1) The first Q & A -- think: ask: so what JS pain points can we think of in business development? Answer, summary, supplement:
- Type restrictions on function parameters;
- Limit the types of arrays and objects to avoid definition errors. For example, array definition errors may occur when data deconstruction is complex or more. A = {}, if (a.length) {/ / xxxxx}
- let functionA = 'jiawen' / / actually let functionA: string = 'jiawen'
3. Make our application code easier to read and maintain. If the definition is perfect, you can roughly understand the function of parameters through types. I believe that through the above simple bug demo, you have a preliminary re understanding of TS. the next chapter will formally introduce how to make good use of TS in the process of business development.
How to use TS
How to use in business TS/How to use it well TS?This problem is actually related to " How to make good use of one in business API " It's the same. First of all, you should know what this thing is doing, what the parameters are, what the rules are, and what extensions can be accepted......wait. In short, roll it! What extensions......wait. In short, roll it!

1. Induction of common types of TS
Through a comprehensive summary of common TS errors in the business, I hope Demos will benefit you
1) string number boolean of primitives
The reason why the author disassembles the basic types is that the nouns primitives / metalinguals / tuples appear frequently in both Chinese and English documents. The author understands the Vernacular: I hope to use literal values instead of built-in object types in the definition of type constraints. Official documents:

let a: string = 'jiawen';let flag: boolean = false;let num: number = 150interface IState: { flag: boolean; name: string; num: number;}
2) Tuple
// Tuple type represents an array with known element number and type. The type of each element does not have to be the same, but the type of the corresponding position needs to be the same. let x: [string, number]; x = ['jiawen', 18]; // ok x = [18, 'jiawen']; // Erro console.log(x[0]); // jiawen
3)undefined null
let special: string = undefined // It is worth mentioning that undefined/null is a subclass of all basic types, // Therefore, they can be assigned to other defined types arbitrarily, which is why the above code does not report an error
4) object and {}
// Object represents the normal Javascript object type, not the basic data type const offDuty = (value: object) => { console.log("value is ", value); } offDuty({ prop: 0}) // ok offDuty(null) offDuty(undefined) // Error offDuty(18) offDuty('offDuty') offDuty(false) // Error // {} represents any type that is not null / undefined const offDuty = (value: {}) => { console.log("value is ", value); } offDuty({ prop: 0}) // ok offDuty(null) offDuty(undefined) // Error offDuty(18) offDuty('offDuty') offDuty(false) // ok offDuty({ toString(){ return 333 } }) // ok // {} is almost the same as Object, except that Object will verify the built-in toString / hasownproperty of Object const offDuty = (value: Object) => { console.log("value is ", value); } offDuty({ prop: 0}) // ok offDuty(null) offDuty(undefined) // Error offDuty(18) offDuty('offDuty') offDuty(false) // ok offDuty({ toString(){ return 333 } }) // Error If an object type is required, but there is no requirement for attributes, it is recommended to use object {} and Object The scope of representation is too large. It is recommended not to use it as much as possible
5)object of params
// We can usually use point object functions (specify parameter object types) in business const offDuty = (value: { x: number; y: string }) => { console.log("x is ", value.x); console.log("y is ", value.y); } // Business must involve "optional attributes"; Let's briefly introduce the convenient and fast "optional attributes" const offDuty = (value: { x: number; y?: string }) => { console.log("Required attribute x ", value.x); console.log("optional attribute y ", value.y); console.log("optional attribute y Method of ", value.y.toLocaleLowerCase()); } offDuty({ x: 123, y: 'jiawen' }) offDuty({ x: 123 }) // Question: is there a problem with the above code? answer: // Offduty ({X: 123}) will cause the result to report an error value. Y.tolocalelovercase() // Cannot read property 'toLocaleLowerCase' of undefined Option 1: Manual type check const offDuty = (value: { x: number; y?: string }) => { if (value.y !== undefined) { console.log("May not exist ", value.y.toUpperCase()); } } Scenario 2: use optional attributes (recommend) const offDuty = (value: { x: number; y?: string }) => { console.log("May not exist ", value.y?.toLocaleLowerCase()); }
6) unknown and any
// unknown can represent any type, but it also tells TS that developers cannot determine the type, so they need to be careful when doing any operation let Jiaven: unknown Jiaven.toFixed(1) // Error if (typeof Jiaven=== 'number') { Jiaven.toFixed(1) // OK } When we use any When you type, any Will escape the type check, and any Variables of type can perform any operation without error during compilation anyscript === javascript be careful: any It will increase the risk of errors during operation and should not be used unless absolutely necessary; If you encounter a scenario that wants to represent [don't know what type], it is recommended to give priority unknown
7) union Union type
union It is also called union type. It is composed of two or more other types. It represents the value that may be any one. It is used between types ' | 'separate type dayOff = string | number | boolean Implicit derivation of union types may lead to errors. Please refer to the language guide for related problems code and tips - <TS Implicit derivation of .It should be noted that an error will be reported when accessing non common attributes, and no error will be reported when accessing common attributes.Last most intuitive demo function dayOff (value: string | number): number { return value.length; } // number does not have length, and an error will be reported. Solution: typeof value = = 'string' function dayOff (value: string | number): number { return value.toString(); } // Both number and string have toString(), and no error will be reported
8)never
// Never is a subtype of other types, including null and undefined, representing values that never appear. // What role does never play in actual development? Here, the author takes you Yuxi's classic explanation as the first example The first example, when you have a union type: interface Foo { type: 'foo' } interface Bar { type: 'bar' } type All = Foo | Bar stay switch Intermediate judgment type,TS It can be narrowed (discriminated union): function handleValue(val: All) { switch (val.type) { case 'foo': // Here val is narrowed to Foo break case 'bar': // val, this is Bar break default: // val is never here const exhaustiveCheck: never = val break } } Attention in default Inside, we narrowed it down to never of val Assign to an explicit declaration never Variable. If all the logic is correct, it should be able to compile here. But if one day your colleague changes All Type of: type All = Foo | Bar | Baz But he forgot to be there handleValue In it is added for Baz Processing logic, At this time default branch inside val Will be narrowed to Baz,Result in failure to assign to never,A compilation error occurred. So in this way, you can ensure handleValue Always exhausted (exhaust) All All Possible types of
The return value of the second usage is never The function of can be the case of throwing an exception function error(message: string): never { throw new Error(message); } The return value of the third usage is never The function of can be an end point that cannot be executed function loop(): never { while (true) {} }
9)Void
interface IProps { onOK: () => void } void and undefined The function is highly similar, but void Indicates that you don't care about the return value of the function or that the method has no return value
10)enum
The author believes that ts Medium enum Is a very interesting enumeration type, and its underlying is number Implementation of 1.General enumeration enum Color { Red, Green, Blue }; let c: Color = Color.Blue; console.log(c); // 2 2.String Enum enum Color { Red = 'red', Green = 'not red', }; 3.Heterogeneous enumeration / Sometimes called mixed enumeration enum Color { Red = 'red', Num = 2, };
<First pit> enum Color { A, // 0 B, // 1 C = 20, // 20 D, // 21 E = 100, // 100 F, // 101 } If the initialization has partial assignment, the value of the subsequent member is the value of the previous member plus 1
<Second pit> This pit is the extension of the first pit. If you are not careful, you will be fooled! const getValue = () => { return 23 } enum List { A = getValue(), B = 24, // You must initialize the value here, or the compilation will not pass C } console.log(List.A) // 23 console.log(List.B) // 24 console.log(List.C) // 25 If the value of a property is calculated, the member following it must initialize the value. Otherwise it will Enum member must have initializer.
11) Generics
The generic type I understand is very Vernacular: first, do not specify the specific type, and get the specific type through the passed parameter type. Let's start with the following filter demo to explore why generics are necessary
Basic styles for generics
function fun<T>(args: T): T { return args }
If you haven't touched it, will you feel a little confused? No problem! We go directly from a business perspective.
1.Initial requirement: filter arrays of numeric types declare function filter( array: number[], fn: (item: unknown) => boolean ) : number[]; 2.The product has changed its requirements: it also filters some strings string[] Speed up, then use the overloading of functions, Add a statement, It's a little stupid, but it's easy to understand declare function filter( array: string[], fn: (item: unknown) => boolean ): string[]; declare function filter( array: number[], fn: (item: unknown) => boolean ): number[]; 3.Here comes the product again! Filter this time boolean[],object[] .......... At this time, if you still choose overloading, the workload will be greatly increased and the code will become more and more cumbersome. At this time, generics will appear, In terms of implementation, it is more like a method to define types through your parameters. The transformation is as follows: declare function filter<T>( array: T[], fn: (item: unknown) => boolean ): T[];
When we understand generics as a method implementation, we naturally associate it with: methods have multiple parameters and default values, and generics can also be used.
type Foo<T, U = string> = { // Multi parameter, default value foo: Array<T> // Can pass bar: U } type A = Foo<number> // type A = { foo: number[]; bar: string; } type B = Foo<number, number> // type B = { foo: number[]; bar: number; }
Since it is a "function", there will also be "restrictions". Some slightly common constraints are listed below.
1. extends: limit T Must be at least one XXX Type of type dayOff<T extends HTMLElement = HTMLElement> = { where: T, name: string }
2. Readonly<T>: Construct a structure with all attributes as readonly,This means that properties of the constructed type cannot be reassigned. interface Eat { food: string; } const todo: Readonly<Eat> = { food: "meat beef milk", }; todo.food = "no food"; // Cannot assign to 'title' because it is a read-only property.
3. Pick<T,K>: from T Pick some from the list K attribute interface Todo { name: string; job: string; work: boolean; type TodoPreview = Pick<Todo, "name" | "work">; const todo: TodoPreview = { name: "jiawen", work: true, }; todo;
4. Omit<T, K>: Combined T and K And ignore the K To construct the type. interface Todo { name: string; job: string; work: boolean; } type TodoPreview = Omit<Todo, "work">; const todo: TodoPreview = { name: "jiawen", job: 'job', };
5.Record: Constraint definition key type is Keys,Value type is Values Object type of the. enum Num { A = 10001, B = 10002, C = 10003 } const NumMap: Record<Num, string> = { [Num.A]: 'this is A', [Num.B]: 'this is B' } // Missing attribute '10003' in type '{10001: string; 10002: string;}', // However, this attribute is required in the type "Record < errorcodes, string >", so we can also conduct comprehensive inspection through Record keyof Keyword can be used to get all of an object type key type type User = { id: string; name: string; }; type UserKeys = keyof User; // "id" | "name" The transformation is as follows type Record<K extends keyof any, T> = { [P in K]: T; }; At this time T by any;
There are some not commonly used but easy to understand: 6. Extract<T, U> from T,U Extract the same type from 7. Partial<T> All properties are optional type User = { id?: string, gender: 'male' | 'female' } type PartialUser = Partial<User> // { id?: string, gender?: 'male' | 'female'} type Partial<T> = { [U in keyof T]?: T[U] } 8. Required<T> All properties must be << === >> And Partial contrary type User = { id?: string, sex: 'male' | 'female' } type RequiredUser = Required<User> // { readonly id: string, readonly gender: 'male' | 'female'} function showUserProfile (user: RequiredUser) { console.log(user.id) // Don't you need to add it at this time? Yes console.log(user.sex) } type Required<T> = { [U in keyof T]-?: T[U] }; -? : Represents removal?
Some notes for TS
1. type and interface of TS
1) interface can only declare object types and supports declaration merging (extensible).
interface User { id: string}interface User { name: string}const user = {} as Userconsole.log(user.id);console.log(user.name);
2) Type (type alias) does not support declaration merge -- l type
type User = { id: string, } if (true) { type User = { name: string, } const user = {} as User; console.log(user.name); console.log(user.id) // Property 'id' does not exist on type 'User'. }

3) Summary of similarities and differences between type and interface:
a. Generally speaking, type is more general, and the right side can be any type, including expression operation, mapping, etc;
b. For those that can be defined by interface, type can also be used;
c. The extension methods are also different. The interface can be extended with the extends keyword or used to implement an interface;
d. Can be used to describe an object or function;
e. Type can declare basic type alias, union type and tuple type, but interface cannot;
f. However, if you are developing a package or module that allows others to expand, use interface. If you need to define basic data types or type operations, use type;
g. The interface can be defined multiple times and will be regarded as a consolidated declaration, but the type does not support it;
h. The export methods are different. The interface supports simultaneous declaration and default export, while the typetype must be declared before export; r/>
2. Script mode and module mode of TS
Typescript has two modes. The logic of distinction is that the file content package does not contain import or export keywords.
1) Script mode (script), a file corresponds to an html script tag.
2) Module mode: a file corresponds to a module of Typescript.
In script mode, all variable definitions and type declarations are global. If multiple files define the same variable, an error will be reported, and the interface with the same name will be merged; In module mode, all variable definitions and type declarations are valid within the module.
There are also differences between the two modes when writing type declarations. For example, in the script mode, declare var GlobalStore can write declarations for global objects directly.
example:
In script mode, declare var GlobalStore can write declarations for global objects directly.
GlobalStore.foo = "foo"; GlobalStore.bar = "bar"; // Error declare var GlobalStore: { foo: string; };
In module mode, declare global is required to write a declaration for a global object
GlobalStore.foo = "foo"; GlobalStore.bar = "bar"; declare global { var GlobalStore: { foo: string; bar: string; }; } export {}; // export keyword changes the mode of the file
3. Index signature for TS
The index signature can be used to define the types of attributes and values in the object. For example, define a React component, allowing props to pass any props with key as string and value as number
interface Props { [key: string]: number } <Component count={1} /> // OK <Component count={true} /> // Error <Component count={'1'} /> // Error
4. Type of TS
Typescript allows you to use a type just as an object takes a property value
type User = { userId: string friendList: { fristName: string lastName: string }[] } type UserIdType = User['userId'] // string type FriendList = User['friendList'] // { fristName: string; lastName: string; }[] type Friend = FriendList[number] // { fristName: string; lastName: string; }
In the above example, we use the function of type typing to calculate several other types from the User type. FriendList[number] number here is a keyword used to get the type of array subitems. You can also use literal numbers in tuples to get the type of array elements.
type group = [number, string] type First = group[0] // number type Second = group[1] // string
5. Assertion of TS
1) Type assertion is not a type conversion. It is not allowed to assert into a type that does not exist in a union type.
function getLength(value: string | number): number { if (value.length) { return value.length; } else { return value.toString().length; } // This problem has been mentioned in the object of parmas and will not be repeated After modification: if ((<string>value).length) { return (<string>value).length; } else { return something.toString().length; } }
Two ways to write assertions 1. <type>value: <string>value 2. perhaps value as string Special attention!!! It is not allowed to assert into a type that does not exist in a union type function toBoolean(something: string | number): boolean { return <boolean>something; }
2) Non null assertion
TypeScript also has a special syntax for removing null and undefined from types without any explicit checking.
Writing after any expression is actually a type assertion, indicating that the value is not null or undefined
function liveDangerously(x?: number | undefined | null) { // Recommended writing console.log(x!.toFixed()); }
How to use TS in Hook components
1,usestate
useState has the ability of type derivation if the initial value is not null/undefined, and infers the type according to the incoming initial value; If the initial value is null/undefined, you need to pass the type definition to constrain. In general, it is recommended to pass in the type (through the first generic parameter of useState).
// Here ts you can infer the type of value and constrain the setValue function call const [value, setValue] = useState(0); interface MyObject { name: string; age?: number; } // Here, you need to pass MyObject to constrain value and setValue // Therefore, we generally recommend incoming types const [value, setValue] = useState<MyObject>(null);
2)useEffect useLayoutEffect
No return value, no type passing and constraints required
3)useMemo useCallback
- useMemo does not need to pass the type, and the type can be inferred from the return value of the function.
- useCallback does not need to pass the type, and the type can be inferred from the return value of the function.
However, note that the input parameter of the function needs to define the type, otherwise it will be inferred as any!
const value = 10; const result = useMemo(() => value * 2, [value]); // It is inferred that result is of type number const multiplier = 2; // Infer (value: number) = > number // Note that the function input parameter value needs to define the type const multiply = useCallback((value: number) => value * multiplier, [multiplier]);
4)useRef
When useRef passes a non null initial value, the type can be inferred. Similarly, the type can be defined by passing in the first generic parameter to constrain the type of ref.current.
1. If the value is null const MyInput = () => { const inputRef = useRef<HTMLInputElement>(null); // Here, the constraint inputRef is an html element return <input ref={inputRef} /> } 2. If not null const myNumberRef = useRef(0); // It is automatically inferred that myNumberRef.current is of type number myNumberRef.current += 1;
5)useContext
useContext generally infers the return value according to the value of the passed in Context. Generally, the delivery type does not need to be displayed.
type Theme = 'light' | 'dark';// We passed the type in createContext const ThemeContext = createContext < theme > ('dark '); Const app = () = > (< ThemeContext. Provider value = "dark" > < mycomponent / > < / ThemeContext. Provider >) const mycomponent = () = > {/ / usecontext infers the type from the ThemeContext. There is no need to display and pass const theme = usecontext (ThemeContext); return < div > the theme is {theme} < / div >
Some thinking
In this paper, the author thinks about the basic application of TS and ts in Hook, but this part is lengthy about how TSC converts TS code into JS code. A separate article can be published later (2) About the bottom implementation of TS generics, this part is more complex, and the author needs to precipitate. You are welcome to leave a message directly or add it in the article!!!