When I first started working with typescript a year ago, I thought it was a lot of work. Write a lot of stuff. Running around looking for a type, introducing third libraries and often reporting errors. But now I want to say: it smells good. We often tease people about how low their code is in terms of maintainability, and we always expect them to write comments on their own initiative, but there is no way to constrain them. Well, types are the best annotations, and with typescript, you can make your code much more maintainable.
I. How to deal with the problems related to third-party library types
Typescipt provides third-party library type definitions that not only constrain our input calls, but also provide us with documentation. Now, there are so many types of third-party type definitions on NPM that it is difficult to guarantee that the type definitions are correct. It is also difficult to guarantee that all third-party libraries used have type definitions. So how do you use Third-Party libraries in TypeScript correctly in this unknown process? Here are four common scenarios where it doesn’t work and how to resolve them:
- The library itself has no built-in type definition
- The library itself has no type definition and no associated @type
- The type declaration library is wrong
- Type declaration error
-
The library does not have its own type definition. Take a chestnut
When you first adapt React to typescript, many of you will encounter module.hot errors. You only need to install the corresponding type library.Install @ types/webpack – env
-
The library itself does not have a type definition, nor does it have an associated @type so you have to declare one yourself. Just give me one.
declareThe module "lodash"Copy the code
- The type declaration library is wrong
- Promote the resolution of official type definition issues, issues, PR
- Import is followed by extending the original type with the extends or merge capability
- Tolerate type loss or unreliability
- / / @ ts – ignore ignored
- Type declaration error
- Add “skipLibCheck”: true to compilerOptions
2. Use type contraction to solve the error
Here are some common solutions:
- Types of assertions
- Typeguard Typeof in instanceof literal type protection
- Double assertion
Type assertions tell TypeScript exactly what type a value is, and in some cases, we’re pretty sure of its type, even if it doesn’t match TypeScript’s inferred type. Then we can use type assertions. The syntax is as follows:
< type > value Value as type // This syntax is recommended. Because <> tends to clash with generics and React syntaxCopy the code
For example, in the following code, the padding value can be either string or number. Even though we write Array() in the code, we know that the padding is converted to number by parseint, but the type definition still fails.
functionPadLeft (value: a string, padding: string | number) {/ / error: Operator'+' cannot be applied to
// types 'string | number' and 'number'
return Array(padding + 1).join("") + value;
}
Copy the code
The workaround is to use type assertions. Tell typescript that I confirm that it is of type number and ignore the error.
functionPadLeft (value: a string, padding: string | number) {/ / normalreturn Array(padding as number + 1).join("") + value;
}
Copy the code
But if we have the following situation, do we have to write a lot of AS?
function padLeft(value: string, padding: string | number) {
console.log((padding as number) + 3);
console.log((padding as number) + 2);
console.log((padding as number) + 5);
return Array((padding as number) + 1).join(' ') + value;
}
Copy the code
2, type guard type guard has the following several ways, simple summary of the following
- Typeof: Determines “number”, “string”, “Boolean”, or “symbol”.
- Instanceof: Used to determine whether an instance belongs to a class
- In: Used to determine whether a property/method belongs to an object
- Literal-type protection
The example above, the string is | number type, so using typeof guards to type. Examples are as follows:
function padLeft(value: string, padding: string | number) {
if (typeof padding === 'number') { console.log(padding + 3); // Normal console.log(padding + 2); // Normal console.log(padding + 5); / / normalreturn Array(padding + 1).join(' ') + value; / / normal}if (typeof padding === 'string') {
returnpadding + value; }}Copy the code
Saves a lot of code compared to the type assertion AS. In addition to Typeof, we have several other methods, the following are examples.
- Instanceof — Used to determine whether an instance belongs to a class
class Man {
handsome = 'handsome';
}
class Woman {
beautiful = 'beautiful';
}
function Human(arg: Man | Woman) {
if (arg instanceof Man) {
console.log(arg.handsome);
console.log(arg.beautiful); // error
} else{// This must be Woman console.log(arg. Beautiful); }}Copy the code
- In — Used to determine whether a property/method belongs to an object
interface B {
b: string;
}
interface A {
a: string;
}
function foo(x: A | B) {
if ('a' in x) {
return x.a;
}
return x.b;
}
Copy the code
- Literal-type protection
For some scenarios, using in, Instanceof, typeof is too cumbersome. You can construct a literal type of your own.
type Man = {
handsome: 'handsome';
type: 'man';
};
type Woman = {
beautiful: 'beautiful';
type: 'woman';
};
function Human(arg: Man | Woman) {
if (arg.type === 'man') {
console.log(arg.handsome);
console.log(arg.beautiful); // error
} else{// This must be Woman console.log(arg. Beautiful); }}Copy the code
Double assertion can also cause errors when using AS, because the assertion of AS is not unconditional. It can be successfully asserted as T only if S type is a subset of T type, or if T type is a subset of S type. So faced with this situation, where you just want to solve the problem by force, you can use double assertion.
function handler(event: Event) {
const element = event as HTMLElement;
// Error: 'Event' 和 'HTMLElement'None of these can be assigned to another}Copy the code
If you still want to use that type, you can use double assertions. First assert that any is compatible with all types
functionhandler(event: Event) { const element = (event as any) as HTMLElement; / / normal}Copy the code
Optimize your code with the latest JS features supported by typescript
- Optional chain Chaining
letx=foo? .bar.baz();Copy the code
The implementation in typescript looks like this:
var _a;
let x = (_a = foo) === null || _a === void 0 ? void 0 : _a.bar.baz();
Copy the code
With this feature, we can save ourselves a lot of nasty CODE like A && A.b && A.B.C
- Nullish Coalescing
let x =foo ?? '22';
Copy the code
The implementation in typescript looks like this:
letx = (foo ! == null && foo ! == void 0 ? foo :'22');
Copy the code
Four. Skillfully use advanced types to deal with data flexibly
Typescript provides some nice utility functions. The following figure
- Type index
To implement the utility functions above, we need to understand the following syntax: keyof: gets a key value on a type extends: a constraint in a generic T[K] : gets the element type of K corresponding to the object T
type Partial<T> = {
[P inkeyof T]? : T[P] }Copy the code
When using props, sometimes all properties are optional, if one by one? A lot of repetition. You can use Partial directly in this case
Record is a particularly flexible tool. The first generic passes in the key value of the object, the second the attribute value of the object.
type Record<K extends string, T> = {
[P in K]: T;
}
Copy the code
Let’s look at this object down here, how would you declare it in terms of TS?
const AnimalMap = {
cat: { name: 'the cat', title: 'cat' },
dog: { name: 'dog', title: 'dog' },
frog: { name: 'frog', title: 'wa'}};Copy the code
In this case, use Record.
type AnimalType = 'cat' | 'dog' | 'frog';
interface AnimalDescription { name: string, title: string }
const AnimalMap: Record<AnimalType, AnimalDescription> = {
cat: { name: 'the cat', title: 'cat' },
dog: { name: 'dog', title: 'dog' },
frog: { name: 'frog', title: 'wa'}};Copy the code
- Never, construction condition type
Except for the syntax above. We can also use never to construct conditional types to combine more flexible type definitions.
Grammar:
// Return X if T is a subtype of U, otherwise return Y constructs the condition type T extends U? X : YCopy the code
typeExclude<T, U> = T extends U ? never : T; // Equivalent to:type A = 'a'
type A = Exclude<'x' | 'a'.'x' | 'y' | 'z'>
Copy the code
- More concise modifiers: – and + can be removed directly? Make all object properties mandatory.
type Required<T> = { [P inkeyof T]-? : T[P] }; // Removereadonly
type MutableRequired<T> = { -readonly [P in keyof T]: T[P] };
Copy the code
- Infer: Type variables to be inferred in the extends condition statement.
// We need to get the value contained in the Promise typetype PromiseVal<P> = P extends Promise<infer INNER> ? INNER : P;
type PStr = Promise<string>;
// Test === string
type Test = PromiseVal<PStr>;
Copy the code
Identify type and interface
In various type libraries, you’ll see a variety of types and interfaces. Yet many people don’t actually know the difference.
The definition on the official website is as follows:
An interface can be named in an extends or implements clause, but a type alias for an object type literal cannot.
An interface can have multiple merged declarations, but a type alias for an object type literal cannot.
Here’s a picture of the difference:
Suggestion: If interface is available, use interface. If not, use type.
Constant enumeration
For sensitive data, use constant enumeration.
const enum learn {
math,
language,
sports
}
Copy the code
Empty after compiling:
Previous excellent articles:
- Typescript basic types
- Typescript enumeration
- The Typescript interface
- Typescript generic
- Typescript functions and classes
- Typescript best practices
Welcome to pay attention to “front-end plus”, seriously learn front-end, do a professional technical people…