This article has participated in the call for good writing activities, click to view: back end, big front end double track submission, 20,000 yuan prize pool waiting for you to challenge!
Said in the previous
The difficulty of this paper is lower than medium. Most of the points involved are how to apply TS reasonably in the project. A small part will involve some principles. **>>>> After reading this article, you may find <<<<**Copy the code
- If you are not familiar with TS, this article will help you complete the TS application section, along with numerous examples to guide business applications.
- If you are familiar with TS, this article can be used as a review, taking you to review the knowledge, hoping to trigger your new discovery and thinking in some points.
- For IState and IProps of class components, part of the writing and thinking of analog Hook components;
πππTIPS: Super handy online TS editor (many configuration items can be manually configured) Portal: TS online π college
What is TS
Without being arcane, TypeScript is, in layman’s terms, a superset of JavaScript that has optional types and can be compiled to run as pure JavaScript. (I’ve always thought of TypeScript as JavaScript Lint.) So why does TS have to be static? Or to put it another way, why do we need to add a type specification to JavaScript?
Classic question and answer session – because it can solve some pain points JS has not solved:
- JS is dynamically typed, which means we don’t know the type of a variable until instantiation, but using TS avoids classic low-level errors before execution. Uncaught TypeError: ‘XXX’ is not a function
β οΈ canonical level error π° :
JS is like this, I’m only told I’m wrong when something goes wrong at runtime, but when TS intervenes:
Boy! Throw the question directly at the editor stage, nice!
- Lazy revelers. The specification is convenient and error-free. For VS Code, the most it can do is indicate whether or not the attribute is available, but it doesn’t say exactly what type the attribute is, but TS can use type derivation/inverse derivation (in plain English: If you don’t explicitly write a type, type inference is used to infer what type you are using), which perfectly optimizes code completion:
First Q&A — Think:
So what other JS pain points can we think of that TS solves in business development? (question)
– Type restrictions on function parameters; – Type restrictions on arrays and objects to avoid definition errors such as complex or large data deconstruction, A = {}, if (a.length){// XXXXX} -let functionA = ‘jiawen’ // let functionA: string = ‘jiawen’
- Make our application code easier to read and maintain, so that if defined well, we can get a rough idea of what parameters are doing by type;
I believe that through the above simple bug-demo, you have a preliminary understanding of TS. The following chapter will formally introduce how to use TS in the business development process
How to use TS
How to use TS in business/How to use TS well? The question is the same as "how to use an API in business". First of all, we need to know what this thing is doing, what parameters are, what rules are, what extensions can be accepted...... And so on. In short, masturbate!Copy the code
TS common type induction
Hopefully you will find Demos useful by providing a comprehensive overview of common TS errors in the business
Primitives string number Boolean
Primitives (primitives, primitives, and primitives) are primitives, primitives, primitives, primitives, primitives, primitives, primitives, primitives, and primitives.Copy the code
let a: string = 'jiawen';
let flag: boolean = false;
let num: number = 150
interface IState: {
flag: boolean;
name: string;
num: number;
}
Copy the code
tuples
// The tuple type represents an array of known elements and types. The elements need not be of the same type, but the corresponding positions should be of the same type.
let x: [string, number];
x = ['jiawen'.18]; // ok
x = [18.'jiawen']; // Erro
console.log(x[0]); // jiawen
Copy the code
undefined null
let special: string = undefined
// It is worth mentioning that undefined/null is a subclass of all basic types,
// So they can be assigned arbitrarily to other defined types, which is why the above code does not report an error
Copy the code
The object and {}
// Object represents a normal Javascript object type, not a 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 non-null/non-undefined type
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 verifies built-in toString/hasOwnPreperty
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}})// ErrorIf an object type is required, but attributes are not required, object {} and are recommendedObjectThe range is too large. Do not use itCopy the code
object of params
// We can usually use the point object function in business (specify the parameter object type)
const offDuty = (value: { x: number; y: string }) = > {
console.log("x is ", value.x);
console.log("y is ", value.y);
}
// There must be "optional attributes" involved in the business; A quick and easy introduction to optional properties
const offDuty = (value: { x: number; y? : string }) = > {
console.log("Required attribute X", value.x);
console.log("Optional attribute Y", value.y);
console.log("Methods for optional property Y", value.y.toLocaleLowerCase());
}
offDuty({ x: 123.y: 'jiawen' })
offDuty({ x: 123 })
// Question: Is there a problem with the above code?The answer:/ / offDuty ({x: 123}) can lead to the error value, y.t oLocaleLowerCase ()
// Cannot read property 'toLocaleLowerCase' of undefinedplan1: Manual type checkconst offDuty = (value: { x: number; y? : string }) = > {
if(value.y ! = =undefined) {
console.log("That may not exist.", value.y.toUpperCase()); }} scheme2: Use optional attributes (recommended)const offDuty = (value: { x: number; y? : string }) = > {
console.log("That may not exist.", value.y? .toLocaleLowerCase()); }Copy the code
Unknown to any
// Unknown can stand for any type, but it also tells TS that developers can't be sure about the type and should be careful when doing anything
let Jiaven: unknown
Jiaven.toFixed(1) // Error
if (typeof Jiaven=== 'number') {
Jiaven.toFixed(1) // OK} When we use any, any escapes type checking, and variables of type any can perform any operations without error at compile time. If you have a scenario where you want to represent unknown, do you prefer unknownCopy the code
Union Union type
A union, also called a union type, consists of two or more other types. It represents a value that may be any one of them'|'Separated type dayOff = string | number | Boolean combination types of implicit derivation may lead to errors, related problems finch please refer to the language code and tips - implicit derivation of TS. Note that an error is reported when accessing a property that is not in common, but not when accessing a property that is in common. The last most intuitive demofunction dayOff (value: string | number) :number {
return value.length;
}
Typeof value === 'string' typeof value === 'string'
function dayOff (value: string | number) :number {
return value.toString();
}
// Both number and string have toString()
Copy the code
never
// never is a subtype of other types, including null and undefined, representing values that never occur.
// How does never work in real development? Here the author of the original copy of yu Xi's classic explanation to do the first exampleFirst example, when you have a union type: interface Foo {type: 'foo'
}
interface Bar {
type: 'bar'} type All = Foo | Bar inswitchTS is the discriminated union of type.function handleValue(val: All) {
switch (val.type) {
case 'foo':
// here val is narrowed to Foo
break
case 'bar':
// val is Bar
break
default:
// val is never
const exhaustiveCheck: never = val
break}} Note indefaultWhere we assign val narrowed to never to a variable explicitly declared never. If everything is logically correct, then this should compile. But if then one day your colleagues changed All types: type All = Foo | Bar | Baz but he forgot in handleValue plus for Baz processing logic, at this timedefaultIn branch, val will be narrowed to Baz, making it impossible to assign to never and causing a compilation error. So in this way you make sure that handleValue always exhausts All possible types of All.Copy the code
The second use of a function that returns never can be a case where an exception is thrownfunction error(message: string) :never {
throw new Error(message); } The third use of a function that returns never can be a terminating point that cannot be executedfunction loop() :never {
while (true) {}}Copy the code
void
interface IProps {
onOK: () = > void
}
void ε undefinedFunction highly similar, butvoidIndicates that it does not care about the return value of the function or that the method has no return valueCopy the code
enum
I think the enum in TS is a very interesting enumeration type, and its underlying is the implementation of number1.Common enum Color {Red, Green, Blue};let c: Color = Color.Blue;
console.log(c); / / 2
2.String Enum Color {Red ='red',
Green = 'not red'};3.Heterogeneous enumeration/sometimes also called mixed enumeration enum Color {Red ='red',
Num = 2};Copy the code
< span style = "max-width: 100%; clear: both; min-height: 1em;/ / 0
B, / / 1
C = 20./ / 20
D, / / 21
E = 100./ / 100
F, / / 101} If the initialization has a partial assignment, the value of the subsequent member is the value of the previous member plus1
Copy the code
< second pit > This pit is an extension of the first pit, a little careful will fall!const getValue = () = > {
return 23
}
enum List {
A = getValue(),
B = 24.// The value must be initialized, or the compilation will fail
C
}
console.log(List.A) / / 23
console.log(List.B) / / 24
console.log(List.C) / / 25If the value of an attribute is computed, the member following it must initialize the value. Otherwise, Enum member must have Initializer will be used.Copy the code
The generic
Let’s start with filter-demo below and explore why generics are necessary
- The basic style of generics
function fun<T> (args: T) :T {
return args
}
Copy the code
Do you feel a little confused if you haven’t? It doesn’t matter! We went straight to the business point of view
1.Initial requirement: Filter array DECLARE of numeric typefunction filter(array: number[], fn: (item: unknown) => boolean) : number[];
2.The product changed the requirement: if you wanted to filter the string[] slowly, you could override the function and add a declaration that was clumsy but understood wellfunction filter(array: string[], fn: (item: unknown) => boolean) :string[];
declare function filter(array: number[], fn: (item: unknown) => boolean) :number[];
3.Product again! This time also filter Boolean [], object[].......... This is where generics come in. It's more like a way to define a type through your pass-throughs: declarefunction filter<T> (array: T[], fn: (item: unknown) => boolean) :T[];
Copy the code
The generic can be any, but most prefer T, U, S, etc.,
When we understand generics as a method implementation, it is natural to think that methods have multiple parameters, default values, and generics can do the same
type Foo<T, U = string> = { // Multiple parameters, 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; }
Copy the code
Since they are “functions,” there are also “limitations.” Here are some of the more common constraints
1. extends: limit T must be of at least one XXX type. Type dayOff<Textends HTMLElement = HTMLElement> = {
where: T,
name: string
}
Copy the code
2.Readonly<T>: Construct an attribute with all attributes Readonly, which means that attributes 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.
Copy the code
3.Pick<T,K>: select some K attributes from Tname: string;
job: string;
work: boolean;
type TodoPreview = Pick<Todo, "name" | "work">;
const todo: TodoPreview = {
name: "jiawen".work: true}; todo;Copy the code
4.Omit<T, K>: Construct type by combining T and K and omitting K from object type. interface Todo {name: string;
job: string;
work: boolean;
}
type TodoPreview = Omit<Todo, "work">;
const todo: TodoPreview = {
name: "jiawen".job: 'job'};Copy the code
5.Record: Constraint defines the object type of Keys and Values. enum Num { A =10001,
B = 10002,
C = 10003
}
const NumMap: Record<Num, string> = {
[Num.A]: 'this is A',
[Num.B]: 'this is B'
}
// Type "{10001: string; 10002: string; }" missing attribute "10003",
// This property is required in the type "Record
", so we can also do a comprehensive check through the Record
,>The keyof keyword can be used to retrieve all key types of an object type. Type User = {id: string;
name: string;
};
type UserKeys = keyof User; // "id" | "name"Type Record<Kextends keyof any, T> = {
[P inK]: T; }; T is any;Copy the code
Here are some less common, but easy to understand:6.Extract<T, U> Extract the same type from T,U7.Partial<T> All attributes Optional Type User = {id? : string,gender: 'male' | 'female'
}
type PartialUser = Partial<User> // { id? : string, gender? : 'male' | 'female'}
type Partial<T> = { [U inkeyof T]? : T[U] }8.Required<T> All attributes must be << === >> opposite to Partial 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) // Do not need to add? the
console.log(user.sex)
}
type Required<T> = { [U inkeyof T]-? : T[U] }; -? : Does it stand for remove?Copy the code
Three, some instructions for TS
TS η type ε interface
- Interfaces can only declare object types and support declaration merge (extensible).
interface User {
id: string
}
interface User {
name: string
}
const user = {} as User
console.log(user.id);
console.log(user.name);
Copy the code
- Type (type alias) does not support declaring merge — L types
type User = {
id: string,
}
if (true) {
type User = {
name: string,
}
const user = {} as User;
console.log(user.name);
console.log(user.id) // Attribute "id" does not exist on type "User".
}
Copy the code
ππππ port interface
- In general, type is more generic. The right side can be any type, including expression operations, mappings, etc.
- Where interface can be defined, type can also be defined.
- Interfaces can be extended using the extends keyword or implements an interface.
- Can be used to describe an object or function;
- Type can declare basic type aliases, union types, and tuples, but interface cannot.
- β οΈ but if you’re developing a package, module that allows others to extend it use interface, if you need to define base data types or need type operations, use type.
- Interface can be defined more than once and is treated as a merge declaration, while type is not supported.
- Interface supports simultaneous declaration and default export, while TypeType must be declared before export.
β
TS script mode and module mode
Typescript has two modes. The logic that distinguishes Typescript is that the file content package does not contain the import or export keyword
A Script file corresponds to an HTML Script tag, and a Module file corresponds to a Typescript Module.
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 modular 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 scripting mode, declare var GlobalStore to write declarations for global objects.
Example:
- Declare var GlobalStore in script mode to write declarations for global objects.
GlobalStore.foo = "foo";
GlobalStore.bar = "bar"; // Error
declare var GlobalStore: {
foo: string;
};
Copy the code
- In modular mode, declare Global is required to write declarations for global objects
GlobalStore.foo = "foo";
GlobalStore.bar = "bar";
declare global {
var GlobalStore: {
foo: string;
bar: string;
};
}
export {}; // The export keyword changes the mode of the file
Copy the code
TS index signature
- Index signatures can be used to define the types of properties and values within objects. For example, defining a React component allows Props to pass any Props with a key of string and a value of number
interface Props {
[key: string]: number
}
<Component count={1} / >// OK
<Component count={true} /> // Error
<Component count={'1'} / > // Error
Copy the code
TS type type
- Typescript allows you to use types just like objects take property values
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; }
Copy the code
- In the example above, we used type typing to calculate several other types from the User type. FriendList[number] The number keyword is used to get the type of the subitem of the array. You can also use literal numbers in tuples to get the types of array elements.
type group = [number, string]
type First = group[0] // number
type Second = group[1] // string
Copy the code
TS of assertion
- Type assertion is not a conversion, and it is not allowed to assert to 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 already been mentioned in the object of ParmasRevised:if ((<string>value).length) {
return (<string>value).length;
} else {
returnsomething.toString().length; }}Copy the code
Two ways of writing an assertion1.< type >value: <string>value2.Or the valueasSpecial attention to string!! It is not allowed to assert a type that does not exist in a union typefunction toBoolean(something: string | number) :boolean {
return <boolean>something;
}
Copy the code
- Non-empty predicate!
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) {
//
console.log(x! .toFixed()); }Copy the code
How to use TS in Hook components
usestate
- If the initial value of useState is not null/undefined, it can deduce the type from the initial value passed in. If the initial value is null/undefined, you need to pass the type definition to constrain it. In general, it is recommended to pass in the type (through the first generic parameter of useState).
// Here ts can infer the type of value and constrain setValue calls
const [value, setValue] = useState(0);
interface MyObject {
name: string; age? : number; }// We need to pass MyObject to constrain value, setValue
// So we generally recommend passing in the type
const [value, setValue] = useState<MyObject>(null);
Copy the code
—–as unkonwn as unkownun
useEffect useLayoutEffect
- No return value, no type passing and no constraints
useMemo useCallback
- UseMemo does not need to pass in the type and can infer the type from the return value of the function.
- UseCallback does not need to pass the type, and can infer the type from the return value of the function.
Note, however, that the function’s input parameter needs to define a type, otherwise it will infer any!
const value = 10;
const result = useMemo(() = > value * 2, [value]); // Deduce that result is of type number
const multiplier = 2;
// Conclude (value: number) => number
// Note that the value parameter needs to define a type
const multiply = useCallback((value: number) = > value * multiplier, [multiplier]);
Copy the code
useRef
- UseRef can infer the type when passing a non-null initial value, and can also define the type by passing the first generic parameter, constraining the type of ref.current.
1.If the passed value isnull
const MyInput = () = > {
const inputRef = useRef<HTMLInputElement>(null); // inputRef is an HTML element
return <input ref={inputRef} />
}
2.If not fornull
const myNumberRef = useRef(0); // Automatically infer that myNumberRef.current is a number type
myNumberRef.current += 1;
Copy the code
useContext
- UseContext generally deduces the return value from the value of the Context passed in. It is generally not necessary to display the delivery type
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 extrapolates the type from ThemeContext. No display-pass is required
const theme = useContext(ThemeContext);
return <div>The theme is {theme}</div>;
Copy the code
Five, some thoughts about TS
1. How does TSC convert TS code into JS code
This part is quite lengthy, so a separate article (2) can be written to explore it.Copy the code
- However, some of the common configuration property sheets for tsconfig.json are worth mentioning
{
"compilerOptions": {
"noEmit": true.// No output file
"allowUnreachableCode": true.// Do not report code errors that cannot be executed.
"allowUnusedLabels": false.// Do not report unused label errors
"alwaysStrict": false.// Parse in strict mode and generate "use strict" statements for each source file
"baseUrl": ".".// Working root directory
"lib": [ // A list of library files that need to be imported during compilation
"es5"."es2015"."es2016"."es2017"."es2018"."dom"
]
"experimentalDecorators": true.// Enable experimental ES decorator
"jsx": "react".JSX is supported in.tsx files
"sourceMap": true.// Whether to generate a map file
"module": "commonjs".// Specify which module system code to generate
"noImplicitAny": false.// Whether to disable any by default
"removeComments": true.// Whether to remove comments
"types": [ The default is automatic import of all declaration files. Once this option is specified, automatic import is disabled and only the specified type declaration files are imported. If an empty array [] is specified, no files are referenced
"node".// Introduce node type declarations]."paths": { // Specify the path of the module, associated with baseUrl, as in webpack resolve. Alias
"src": [ Import * from 'SRC ';
"./src"],},"target": "ESNext".// What version is the target for compilation
"outDir": "./dist".// Output directory
"declaration": true.// Whether to automatically create a type declaration file
"declarationDir": "./lib".// The output directory of the type declaration file
"allowJs": true.// Allows javascript files to be compiled.
},
// Specify a list of matches (belonging to all ts related files in the automatically specified path)
"include": [
"src/**/*"].// Specify an exclusion list (reverse of include)
"exclude": [
"demo.ts"].// Specify which files to use this configuration (manually specify files one by one)
"files": [
"demo.ts"]}Copy the code
2. The underlying implementation of TS generics
About TS generic advanced article links: [https://dtstack.yuque.com/rd-center/sm6war/wae3kg] (https://dtstack.yuque.com/rd-center/sm6war/wae3kg)Copy the code
β
This part is more complicated, the author still needs precipitation, welcome to leave a message directly or add in the article!!Copy the code