Summary of knowledge points

Vue3 needs to use TS may be the biggest obstacle for partners not to start playing vue3. After 1 year of vue3+ TS development practice, I summarized the TS knowledge points that must be known (only 1/4 of the content of the official website), which is convenient for everyone to refer to when learning vue3, and ensure that everyone learned to play VUe3 easily in this article.

🌕 I wish you a happy Mid-Autumn Festival

Vue3 basis

If you haven’t started vuE3 yet, you can also check out the basic vuE3 tutorial I put together (with videos).

Underlying data types

The code in this course is available in thehereTest it out and see what js looks like with TS compiled. www.typescriptlang.org/zh/play

Type annotation

In typescript we can type variables, and in subsequent code TS checks for incorrect reads/writes.

let is:boolean;
is =  true;
is =  123; // Error: Type number cannot be assigned to Boolean variables
Copy the code

The syntax is simple: you add “:” and type to the end of a variable. This action is called type annotation.

Automatic type inference

If the value of a variable is a literal, TS can automatically infer the type.

let is = false;
is = true;
is =  123; // Error: Type number cannot be assigned to Boolean variables

let o = {a:1.b:'2'}
o.a ='33' String cannot be assigned to number
Copy the code

​

literal

Literals are representations of data that we know when we see them, such as the data above to the right of the “=”. The concept itself is very simple, but I put it up separately just in case the students who hear the word for the first time will be confused.

Other situations

There are several other cases where TS will automatically infer variable types, which we’ll expand on next time.

Underlying data types

Now let’s look at what data types are available in the system.

boolean

Boolean type.

let is:boolean;
is =  true;
is =  123; // Error indicating that is is a number that cannot be assigned to a Boolean variable
Copy the code

number

Numeric types that support more than base 10.

let decLiteral: number = 6;
let hexLiteral: number = 0xf00d;
let binaryLiteral: number = 0b1010;
let octalLiteral: number = 0o744;
decLiteral = '123'; / / an error
Copy the code

string

The value is a string.

let s1:string = 'hello world! '; 
let s2:string = 'hello ${name}`; S1 = 123 // ErrorCopy the code

An array of

Array types can be represented in two ways.

“Type + []”

let numbers:number[] = [1.2.3.4.5];

/ / number | string on behalf of the joint types, the following types of senior can speak
let numbers:(number|string) [] = [1.2.3.4.'5'];
Copy the code

Array < type >

let numbers:Array<number> = [1.2.3.4.5];
Copy the code

Tuples (a Tuple)

A tuple type represents an array with a known number and type of elements. The elements need not be of the same type:

let list1:[number.string] = [1.'2'.3]; // The number of elements in a tuple is not correct
let list2:[number.string] = [1.2]; // error, the second element is of the wrong type, should be string '2'
let list3:[number.string] = ['1'.2]; // Error, the types of two elements are reversed
let list4:[number.string] = [1.'2']; / / right
Copy the code

Enumeration (enum)

Enumerations are types that are present in ts but not in js, and will be converted to objects when compiled. The default element values start at 0, as follows:

enum Color {Red, Green, Blue}
/ / equivalent
enum Color {Red=0, Green=1, Blue=2}
Copy the code

We can also reverse the value to the key:

enum Color {Red=1, Green=2, Blue=4}
Color[2= = ='Green' // true
Copy the code

Take a look at the js enumeration code to see why you can get the keys backwards:

var Color;
(function (Color) {
    Color[Color["Red"] = 0] = "Red";
    Color[Color["Green"] = 1] = "Green";
    Color[Color["Blue"] = 2] = "Blue";
})(Color || (Color = {}));
/ / Color value is: {0: "Red", rank 1: "Green", 2: "Blue", Red: 0, Green: 1, Blue: 2}
Copy the code

Any (any type)

Any stands for any type. That is, if you don’t know what type a variable is, you can mark it with any. For example, some older JS libraries that don’t declare a type can mark it with any, so that TS doesn’t get an error. Of course, you can’t use ‘any’ everywhere, because ts doesn’t make sense.

void

Void, as opposed to any, is not of any type. It is used in functions to indicate that a function has no return value: void void

function abc(n:number) :void{
    console.log(n);
}
Copy the code

Void (undefined, null); void (undefined, null);

const n1:void = undefined;
const n2:void = null;
Copy the code

Null, and undefined

By default null and undefined are subtypes of all types, such as:

const n1:null = 123;
const n2:undefined = '123';
Copy the code

never

Never means unreachable and is used in the case of throw:

function error() :never{
    throw 'wrong! ';
}
Copy the code

unknown

Unknown indicates the unknown type. Like any, the labeled variable can be assigned any value, but the type of the variable must be asserted before accessing the attribute of the variable.

let a:unknown;
a = 'characters';
a.length // The message type is unknown and length cannot be accessed
Copy the code

Type assertion (as or <>)

I can force TS to tell what type the variable is now when I operate. For example, the a variable above is unknown, and we cannot operate on it in TS. In this case, we need to perform type assertion:

let a:unknown;
a = 'characters';

(a as string).length

//
(<string>a).length
Copy the code

The two grammars have the same effect, I suggest you use the first one, it is easier to write.

symbol

let s:symbol;
s = Symbol('a');
s = 1 / / an error
Copy the code

object

Object represents a non-primitive type, i.e. a type other than number/ string/ Boolean/symbol/ null/ undefined:

let o1:object = [];
let o2:object = {a:1.b:2};
Copy the code

In fact, we don’t use the object type, because it doesn’t specify the type. Instead, we use interfaces to specify more specific object types. Next time, we’ll talk about how to define interfaces.

Automatic type inference (type protection)

In the last section we said that TS can infer variable types from “literals.” This ability to “automatically infer” is officially called “type protection.”

let o = {a:1.b:'2'} // Recognize fields and types in objects
o.a ='33' String cannot be assigned to number
Copy the code

In this section we continue with other situations where automatic inference can be triggered.

typeof

Determine the type.

let n:number|string = 0.5 < Math.random()? 1:'1';

// If there is no typeof, n*=2 will cause an error, indicating that the current type is number and the multiplication cannot be performed
if('number'= = =typeof n) {
    n*= 2;
} else  {
    n= '2';
}
Copy the code

Typeof in type

“Typeof” is the API in js. In fact, there is also a “typeof” keyword in ts, which can extract the typeof js objects.

function c(n:number){
  return n;
}

// typeof c gets the typeof function c
function d(callback :typeof c){
  const n = callback(1);
  console.log(n);
}
Copy the code

In vscode’s hint we can see that ts has correctly derived the typeof “typeof c”.

instanceof

Check if it is an instance.

let obj = 0.5 < Math.random() ? new String(1) : new Array(1);

if(obj instanceof String) {// obj is inferred to be a String
    obj+= '123'
} else {
    // obj is of type any[]
    obj.push(123);
}
Copy the code

in

Check whether a field exists.

interface A {
  x: number;
}

interface B {
  y: string;
}

function ab(q: A | B) {
  if ('x' in q) {
    // q: A
  } else {
    // q: B}}Copy the code

Custom Type Protection (IS)

We use the system’s own keywords to trigger the “type protection “, below we define a custom:

  1. First we need to define a function that evaluates and returns Boolean values.
  2. The return value should not be marked Boolean. Instead, use “is”, which is usually preceded by a parameter and followed by the determined type.
  3. For example, in the following “isBird” function, if the return value is true, then “animal” isBird.
  4. Automatic type inference is triggered when “isBird” is used with “if”.
interface Animal {
    name: string;
}

interface Bird {
    name: string;
    hasWing: boolean;
}

// It depends on the type annotation of the function return value
function isBird(animal:Animal) :animal is Bird {
    return animal.name.includes('birds');
}


// Use isBird pairs to trigger automatic type inference
const who = { name: 'Canary' };
if (isBird(who)) {
    who.hasWing = true;
}
Copy the code

​

“Is “is used in vuE3 source code

Understand.

// Whether it is an object
export const isObject = (val: any): val is Record<any.any> => val ! = =null && typeof val === 'object'

// Whether to ref objects
export function isRef(v: any) :v is Ref {
  return v ? v[refSymbol] === true : false
}

/ / whether the vnode
export function isVNode(value: any) :value is VNode {
  return value ? value._isVNode === true : false
}

// Whether to slot a node
export const isSlotOutlet = (
  node: RootNode | TemplateChildNode
): node is SlotOutletNode =>
  node.type === NodeTypes.ELEMENT && node.tagType === ElementTypes.SLOT
Copy the code

interface

A format for defining complex types, such as object types:

interface Article {
    title: string;
    count: number;
    content:string;
    fromSite: string;
}

const article: Article = {
    title: 'Learn typescript(2), type for vue3'.count: 9999.content: 'xxx... '.fromSite: 'baidu.com'
}

/ / an error does not exist titleA field, at the same time, lack of the title/count/content/fromSite
const article: Article = {
    titleA: 'Learn typescript(2), type for vue3',}Copy the code

When assigning a value to article, ts will prompt you with an error if any field is not assigned or if the field corresponds to the wrong data type.

scalability

For example, the “A” field and “b” field of an Article may also be allowed to be unknown.

interface Article {
    title: string;
    count: number;
    content:string;
    fromSite: string;
  	[k:string] :string;
}
Copy the code

With “[k:string]:string” we allow any string key in the Article type, as long as the content is also a string.

Not required (?)

With “?” The tag fromSite field is optional.

interface Article {
    title: string;
    count: number;
    content:string; fromSite? :string; / / not required
}

// The missing fromSite field will not cause an error
const article: Article = {
    title: 'Learn typescript(2), type for vue3'.count: 9999.content: 'xxx... ',}Copy the code

Read-only (readonly)

Use “readonly” to mark fields as unmodifiable.

interface Article {
    readonly title: string;
    count: number;
    content:string; fromSite? :string; / / not required
}

let a:Article =  {title:'title'.count:1.content:'content',}
a.title = 123; // Error: title is read-only
Copy the code

Define a function

// Declare the interface
interface Core {
    (n:number.s:string) :number.string]}// Annotate with interface
const core:Core = (a,b) = >{
    return [a,b];
}
Copy the code

Functions are also objects, so Core can also declare properties.

interface Core {
    (n:number.s:string) :number.string];
		n:number;
}

const core:Core = (a,b) = >{
    return [a,b];
}
core.n = 100;
Copy the code

Define the class

Constraint classes use the “implements” keyword.

/ / define
interface Animal {
    head:number;
    body:number;
    foot:number;
    eat(food:string) :void;
    say(word:string) :string;
}

// implements
class Dog implements Animal{
    head=1;
    body=1;
    foot=1;

    constructor(head:number){
        this.head = head;
    }
    eat(food:string){
        console.log(food);
    }
    say(word:string){
        returnword; }}Copy the code

The interface section specifies the instance attribute of the class, while the constructor is not defined.

inheritance

Make one interface have all the features of another.

interface Animal {
    color: string;
}

interface Cat extends Animal {
    foot: number;
}

let cat:Cat;
cat.color = "blue";
cat.foot = 10;
Copy the code

merge

Interfaces with the same name are automatically merged.

interface Apple {
    color: string;
}
interface Apple {
    weight: number;
}
Copy the code

Is equivalent to

interface Apple {
    color: string;
    weight: number;
}
Copy the code

Type Alias (Type)

Use the “type” keyword to rename an existing type.

type N = number;
type S = string;

interface F1{
	(a:number) :string;
}
type F2 = F1;
Copy the code

At first glance, you might not think this is very useful, and it’s possible to define the function “interface”, so here’s what makes it special.

Define a function

As mentioned earlier, interfaces can define functions, and so can types, but the syntax is slightly different:

interface F1{
	(a:number) :string;
}

type F2 = (a:number) = >string;
Copy the code

“F1” and “F2” define the same type, the difference between the main argument and the return value, one is “:” one is “=>”.

Define literal types

That’s what “type” can do that “interface” can’t.

type N = 1|2|3;
type S = 'a'|'b'|'c';

/ / right
const n1:N = 1;
// Error, no 11 in type N;
const n2:N = 11
Copy the code

The joint type (|)

The multiple types with “|” together, “or” the relationship between said type.

type F1 = (a:string|number) = >any;
const f1:F1 = (a) = >a;

/ / right
f1(1);
f1('A');

// Error, argument type should be string or number
f1({a:1});
Copy the code
interface Cat  {
  	hand:string;
    foot: string;
}

interface Duck{
	body:string;
}

type Animal  = Cat|Duck;

// error, Cat has no body field
const animal:Cat = {hand:'hand'.body:The 'body'.foot:'feet'};
/ / right
const animal:Animal = {hand:'hand'.body:The 'body'.foot:'feet'};
Copy the code

Cross type (&)

Concatenate multiple types with “&” to take the union of the types.

interface A {a:number};
interface B {b:string};
type AB = A & B;

const a:A = {a:1};
const b:B = {b:'1'};
// Error, missing a field
const ab1:AB = {b:'2'};
/ / right
const ab2:AB = {a:1.b:'2'};
Copy the code

The generic

Generics can be understood as using “undefined types” to describe classes/functions/interfaces, and then specifying the specific types as they are used. This “indeterminate type,” we call it a type variable, and it can represent any type, and we usually use capital letters for types, like “T” or “U”.

function echo<T> (input:T) :T{
    return input;
}
Copy the code

Generic function

Above we define a **” generic function “. First we define the type variable T, surrounded by “<>”, and then mark the parameter and return value of the function as “T”**. The “input” argument can be of any type with “echo”, but unlike the “any” annotation, the return value of the function changes synchronously based on the type of the argument.

// n1 is a number type
const n1 = echo<number> (1);

// s is inferred to be a string
const s = echo<string> ('1') 
Copy the code

If the argument is literal, omit the preceding “” and “” when using the function, ts can automatically infer the type of the argument, and thus infer the value of the type variable T:

// n1 is a number type
const n1 = echo(1);

// s is inferred to be a string
const s = echo('1') 
Copy the code

A generic class

Declare a type variable U after the class name with “<>”. Methods and attributes of the class can use this type U.

class Person<U> {
    who: U;
    
    constructor(who: U) {
        this.who = who;
    }

    say(code:U): string {
        return this.who + ' :i am '+ code; }}Copy the code

Next we use the following generic class:

let a =  new Person<string> ('James Bond');
a.say(007) // The argument should be a string
a.say('007') / / right
Copy the code

We specify type variable (string) and tell ts that U of this class is string. By definition of Person, we know that the argument to the say method is also string. So we can constrain generics by passing in a type variable.

Generic method

The same way generic functions are defined:

class ABC{
    // Enter T[] to return T
    getFirst<T>(data:T[]):T{
        return data[0]; }}Copy the code

The generic type

We can use a type variable to describe a ** type. ** can be implemented by combining type and interface:

type

type A<T> = T[];
/ / right
const a: A<number> = [1.2.3]
/ / error
const b: A<number> = ['1'.'2'.'3'];
Copy the code

Generic Interfaces

interface Goods<T>{
    id:number;
    title: string;
    size: T;
}

/ / right
let apple:Goods<string> = {id:1.title: 'apple'.size: 'large'};
let shoes:Goods<number> = {id:1.title: 'apple'.size: 43};
Copy the code

The default value

type A<T=string> = T[];
/ / right
const a  = ['1'.'2'.'3'];

interface Goods<T=string>{
    id:number;
    title: string;
    size: T;
}
/ / right
let apple:Goods<string> = {id:1.title: 'apple'.size: 'large'};
Copy the code

Generic constraint

function echo<T> (input: T) :T {
    console.log(input.name); // Error, T is not determined by the name attribute
    return input;
}
Copy the code

We said that T can represent any type, but not all types have a “name” field. “extends” constrains the scope of “T” :

// now T is a type with a name attribute
function echo<T extends {name:string}>(input: T): T {
    console.log(input.name); / / right
    return input;
}
Copy the code

Multiple type variables

You can use multiple type variables at the same time.

function test<T.U> (a:T,b:U) :T.U]{
	return [a,b];
}
Copy the code

Do not abuse generics

The main purpose of generics is to constrain, or narrow down, types. If you can’t constrain functionality, it means you don’t need generics:

function convert<T> (input:T[]) :number{
    return input.length;
}
Copy the code

It makes no sense to use generics, just like any.

Tool type

There are many preset tool types.

Partial

Make properties optional

type A  = {a:number.b:string}
type A1 = Partial<A> // { a? : number; b? : string; }
Copy the code

Required

Make attributes mandatory.

typeA = {a? :number, b? :string}
type A1 = Required<A> // { a: number; b: string; }
Copy the code

Pick<T,K>

Only the selected attributes are reserved. K represents the attribute key value to be reserved

type A  = Pick<{a:number.b:string.c:boolean}, 'a'|'b'>
type A1 = Pick<A, 'a'|'b'> // {a:number,b:string}
Copy the code

Omit<T,K>

The implementation excludes selected properties.

type A  = {a:number.b:string}
type A1 = Omit<A, 'a'> // {b:string}
Copy the code

Record<K,T>

Create a type where K represents the type of the key value and T represents the type of the value

type A1 = Record<string.string> / / equivalent {[k: string] : string}
Copy the code

Exclude<T,U>

Filter T types that are the same as (or compatible with) U

type A  = {a:number.b:string}
type A1 = Exclude<number|string.string|number[] >// number

/ / compatible
type A2 = Exclude<number|string.any|number[] >// never, number is filtered because any is compatible with number
Copy the code

Extract<T,U>

Extract the same (or compatible) type of T and U.

type A  = {a:number.b:string}
type A1 = Extract<number|string.string|number[] >// string
Copy the code

NonNullable

Remove undefined and null from T.

type A1 = NonNullable<number|string|null|undefined> // number|string
Copy the code

ReturnType

Gets the type of the return value of T.

type A1= ReturnType<() = >number> // number
Copy the code

InstanceType

Returns the instance type of T

There are two types of classes in ts, the type of the static part and the type of the instance, so if T is a constructor type, then InstanceType can return its InstanceType:

interface A{
    a:HTMLElement;
}

interface AConstructor{
    new():A;
}

function create (AClass:AConstructor) :InstanceType<AConstructor>{
    return new AClass();
}
Copy the code

Parameters

Gets the function argument type, the return type is primitive, the element order is the same as the argument order.

interface A{
    (a:number.b:string) :string[];
}

type A1 = Parameters<A> // [number, string]
Copy the code

ConstructorParameters

Gets the type of the constructor argument, similar to Parameters, except T is the constructor type.

interface AConstructor{
    new(a:number) :string[];
}

type A1 = ConstructorParameters<AConstructor> // [number]
Copy the code

More and more

In fact you can in * * “node_modules/typescript/lib/lib. Es5. Which s” * * found in all types of definition above, with the renewal of the ts, here may have more types.

Custom tool types

Last time we looked at a number of preset tool types for the system. Next we’ll look at implementing tool types ourselves.

keyof

Look at the source code for the system default Partial.

type Partial<T> = {
    [P inkeyof T]? : T[P]; };Copy the code

Keyof (); keyof (); keyof ();

type A = keyof {a:string.b:number} // "a"|"b"
Copy the code

in

Used to traverse union types.

type A = "a"|"b"
type B = {
    [k in A]: string;
}
// type B:
{
    a: string;
    b: string;
}
Copy the code

Extends and triadic operations

We previously used “extends” to constrain type ranges in the “** Generic constraints “section. Here we combine the ternary operation to make a type conditional judgment. Grammar:

type A = T extends U ? X : Y;
Copy the code

If the range of U is greater than T, such as all fields T of U, then the value of A is X, and vice versa.

type T1 = {a:number.x:number};
type U1 = {a:number.b:number};
type A1 = T1 extends U1 ? number : string; // string

type T2 = {a:number.b:number};
type U2 = {a:number};
type A2 = T2 extends U2 ? number : string; // number
Copy the code

The system default type “Exclude” also implements this:

type Exclude<T, U> = T extends U ? never : T;
Copy the code

Here means that if T has the same type as U when filtered out:

type T1 = number|string;
type U1 = number|string[];
type A = Exclude<T1,U1> // string
Copy the code

Infer (type inference)

The word itself means “to infer “, which means to declare the type variable to be inferred in the extends condition statement. Let’s take a look at the default type Parameters:

type Parameters<T extends(... args:any) = >any> = T extends(... args: infer P) =>any ? P : never;
Copy the code

A P is declared to represent… Possible types of args if (… Args: infer P) range is greater than T, then return… The corresponding type of args, which is the parameter type of the function, returns never. Note: the initial T extends (… Args: any) => any The input T must be a function.

Application of infer

Infer next we use infer to “remove the first element of a meta-ancestor type “:

export type Tail<T extends unknown[]> = T extends [a: any. args: infer P] ? P :never

type A = Tail<[number.string.null] >// [string,null]
Copy the code

Type Assertion (AS)

In some cases, the system cannot automatically infer the correct type, so we need to mark it ourselves:

document.body.addEventListener('click'.e= >{
    e.target.innerHTML = 'test'; // Error because e.target is of type EventTarget and has no innerHTML on it
    (e.target as HTMLElement).innerHTML = 'test';/ / right
});
Copy the code

Because the “addEventListener” method is not only supported by DOM elements, the system type description of “e. tag “cannot be labeled as “HTMLElement” directly.

This is a dom element, so we need to tell ts:” This is a DOM “. This is a type assertion. The syntax is “as”, and we can also use “<>” :

document.body.addEventListener('click'.e= >{
    (e.target as HTMLElement).innerHTML = 'test';
  	/ / equivalent
  	(<HTMLElement>e.target).innerHTML = 'test';
});
Copy the code

Application in AXIos

Axios is the most commonly used HTTP request library for development. The type of the data we request asynchronously must be unknown, so we need to mark the type:

axios.get<{n:number} > ('/xx').then(data= >{
    console.log(data.data.n+1);
});
Copy the code

The “get” method is defined using a generic type. The type passed in “<>” represents the type of returned data, which is equivalent to the annotation return value type.

But if you use the “Axios interceptor” to change the return value, then the annotation here will not work well, such as:


http.interceptors.response.use(function (response) {
   Return the value of the data field
   return data.data;
}, function (error) {
    return Promise.reject(error);
});
Copy the code

Since we modify the return value, axios doesn’t know that its own return value has changed, so it can’t label the return value type as above. In this case, we can use type assertion:

axios.get('/xx').then(data= >{
    console.log((data as {n:number}).n+1);
});
Copy the code

Enumeration (Enum)

Enumerations are typescript’s own data type, which does not exist in javascript, but there is no compatibility problem because enumerations are compiled into JS code after compilation.

Use the “enum” keyword to define enumerations,Enum can be formatted like Class, with members assigned by “=”:

enum Direction {Up, Right, Down, Left};
Copy the code

Look at the compiled JS code:

var Direction;
(function (Direction) {
    Direction[Direction["Up"] = 0] = "Up";
    Direction[Direction["Right"] = 1] = "Right";
    Direction[Direction["Down"] = 2] = "Down";
    Direction[Direction["Left"] = 3] = "Left";
})(Direction || (Direction = {}));
Copy the code

From here we can see that the values and keys as enumerators are both assigned as the keys of the object, which is actually the object we are accessing, so enumerations support bidirectional access:

Direction[0] // 'Up'
Direction.Up / / 0
Copy the code

Application scenarios

Mainly used for constant definitions, enumerations act as scopes, which are more readable, and ts can infer their types more clearly.

Increasing the value

Enumeration members whose values are not specified increment from 0 by default:

Direction.Up / / 0
Direction.Right / / 1
Direction.Down / / 2
Direction.Left / / 3
Copy the code

If a member is assigned a number, then increments from that number:

enum Direction {Up=5, Right, Down=10, Left};
Direction.Up / / 5
Direction.Right / / 6
Direction.Down / / 10
Direction.Left / / 11
Copy the code

The value can only be number and string

/ / right
enum Direction {Up = 'on', Right = 1};

/ / error
enum Direction {Up = ['] on ', Right = {r:1}};
Copy the code

It can also be calculated

If the result of the expression is number or string, it can also be used as an enumerator:

function greenValue(){
    return 2;
}

enum Color {Red = 1, Green = greenValue(), Blue='aaa'.length};
Copy the code

Note, however, that if a member has no value, then the member preceding it must have the literal value:

function greenValue(){
    return 2;
}
// Error: Blue requires Green to be a literal number or string
enum Color {Red = 1, Green = greenValue(), Blue};

// No problem, there is no member before the first member, so it is not restricted by the above
enum Color1 {Red, Green = greenValue(), Blue='aaa'.length}; 
Copy the code

Constant enumeration (const)

Declare constant enumerations using “const enum”,

const enum Enum {
  A = 1,
  B = A * 2,
}

Enum.A
Enum.B
Copy the code

Unlike ordinary enumerations, constant enumerations do not generate redundant code after being compiled into JS:

// The compiled result
"use strict";
1 /* A */;
2 /* B */;
Copy the code

I personally prefer constant enumerations in development, because they don’t generate redundant code, while maintaining good semantics.

What is a declaration file

If a TS project introduces js files, it needs to write a “declaration file” to tell TS to import the variable types in the JS file.

Application scenarios

  1. If a third-party JS library such as “Jq” is introduced in HTML, the use of “variables ∗∗ needs to be declared ∗∗” in TS code, since TS does not know that” variables ∗∗ needs to be declared ** *”, since TS does not know that “variables ∗∗ needs to be declared **”.
  2. Add missing (or custom) fields on global objects such as “window/doucument”.
  3. An existing JS package in NPM, supplementing its type declaration.
  4. Extensions to the types of TS packages that already exist on NPM, such as the $xx attribute added to vue instances, are self-declared in TS.

For example

The implementation code for the first case is as follows:

// global.d.ts
declare var $: (selector: string) = > any;
Copy the code

Here **”declare”** is the keyword of the declaration. Other scenario examples will be covered in later lessons.

Declaration file on NPM

Jq: @types/jquery: lodash: @types/lodash: @types/lodash: If the package you install through NPM does not have a declaration file you can execute the corresponding installation command:

npm i @types/xx
Copy the code

File location

Declaration files are generally placed inProject root directory, so that the entire project ts file can read the declared type, named in the format of “xx.d.ts”.

Global interface/Type

We can also add an interface and a type to “global.d.ts”, which makes the interface global, so that the type can be read in any file in the project.

// global.d.ts
interface Abc{
	n:number
}
Copy the code
// index.ts
const abc:Abc = {n:100};
Copy the code

Introduction (/ / /)

If you want to reuse an existing type, we can use the “///” syntax to import it:

// test.d.ts
type ABC = number;
Copy the code
// global.d.ts
/// <reference path="./test.d.ts" />
declare const a:ABC // number
Copy the code

Now that we have a basic understanding of the declaration file, the next section begins by learning to write your own declaration file.

Declaration of global variables

For declarations of global variables we use”declare“Keyword implementation. Again, declaration files are usually placed in the root directory of the project, called “global.d.ts” for semantic purposes.The following shows declarations for various types of data.

declare var

Declare global variables

// global.d.ts
declare var n:number;
declare let s:string;
declare const version:string;
Copy the code

Var can also be “let” or “const”, the same semantics as js, depending on the situation.

declare function

Declaring global functions

// global.d.ts
declare function translate(words: string) :string;
Copy the code

declare enum

Declare global enumeration

// global.d.ts
declare enum Directions {
    Up,
    Down,
    Left,
    Right
}
Copy the code

declare class

Declaring a global class

// global.d.ts
declare class Animal {
    name: string;
    constructor(name: string);
    sayHi(): string;
}
Copy the code

declare namespace

Declaring a namespace can be interpreted as declaring an object.

// global.d.ts
declare namespace req {
    function get(url: string, params? : Record<string.any>) :Promise<any>;
}
Copy the code

Extend global variable

If you extend a global variable, you need to declare the expanded field. For example, add the cut method to “String”.

String.prototype.cut = function(n){
    return this.substr(0,n);
};

'12345'.cut(3); / / 123
Copy the code

Corresponding declaration file:

// global.d.ts
interface String {
  // Customize the field
  cut(s:string) :string;
}
Copy the code

Because the “String” type is written in the ts declaration file, which itself is described by interface, rememberInterface types are automatically mergedI’m going to write an interface of the same name to merge types.

System declaration file

Js API in TS is all type declaration, we can in the project”node_modules/typescript/lib/“To see all system declaration files. You can also check it out on Github

API with browser prefix

I have noticed that the system does not declare “browser prefix” API, if we write our own plugin will need to supplement this part, such as “requestFullScreen” API:

// global.d.ts
interfaceHTMLElement { webkitRequestFullscreen(options? : FullscreenOptions):Promise<void>; webkitRequestFullScreen(options? : FullscreenOptions):Promise<void>; msRequestFullscreen(options? : FullscreenOptions):Promise<void>; mozRequestFullScreen(options? : FullscreenOptions):Promise<void>;
}
Copy the code

Source: github.com/any86/be-fu…

Extending types of NPM modules (Declare Module)

The “package” downloaded by NPM comes with a declaration file. If we need to extend its type declaration, we can use the “Declare Module” syntax.

Let vue3 support this.$axios

// main.ts
app.config.globalProperties.$axios = axios;
Copy the code

Functionally we implement “this. axios”, but ts does not automatically infer that we added axios. Using this.$axios within a component will prompt you for a missing type, so add the following declaration:

// global.d.ts

import { ComponentCustomProperties } from 'vue'

// The instance type of axios
import { AxiosInstance } from 'axios'

// Declare the extension to @vue/ run-time core package.
/ / expansion "ComponentCustomProperties" interface here, because he is the instance of vue3 attribute type.
declare module '@vue/runtime-core' {
  
  // Provide the type for 'this.$HTTP'
  interface ComponentCustomProperties {
    $axios: AxiosInstance; }}Copy the code

Expansion of “ComponentCustomProperties” interface here, because he is in vue3 instance of the type of the property.

A more comprehensive example

In the example above we extend the interface in the original declaration, but what if the export is a Class? The default export for “any-touch” is a Class. Suppose we make the following changes to the “any-touch” code:

  1. Export adds “AAA” variable, which is string.
  2. Class instance adds the “BBB” attribute, which is of type number.
  3. Class adds the static attribute “CCC “, which is a function.
// global.d.ts

// AnyTouch must import, because only import is an extension, not import will become an overwrite.
import AnyTouch from 'any-touch'

declare module 'any-touch' {
    // Export adds the "aaa" variable, which is a string.
    export const aaa: string;
		
    export default class {
      // Class adds a static attribute "CCC ", which is a function.
      static ccc:() = >void
      // Add a "BBB" attribute to an instance of the class, which is type number.
      bbb: number}}Copy the code

Note: AnyTouch must import, because only an import is a type extension, otherwise it becomes an overwrite.

Under test, the types have been added correctly:

// index.ts
import AT,{aaa} from 'any-touch';

const s = aaa.substr(0.1);

const at = new AT();
at.bbb = 123;

AT.ccc = () = >{};
Copy the code

Type extension for non-TS/JS file modules

Ts only supports importing and exporting modules, but sometimes you may need to import CSS/HTML files. In this case, you need to use wildcards to make TS regard them as modules. Here is the import support for “.

// global.d.ts
declare module '*.vue' {
  import { DefineComponent } from 'vue'
  const component: DefineComponent<{}, {}, any>
  export default component
}
Copy the code
// App.vue
// Can recognize vue files
import X1 from './X1.vue';
export default defineComponent({
	components:{X1}
})
Copy the code

Declare the vue file as a module and note that the default export of the module is of type “Component”. This allows the registration module to correctly identify the type in the Components field of the Vue.

vuex

$store = $store = $store = $store = $store = $store = $store

// vuex.d.ts

import { ComponentCustomProperties } from 'vue'
import { Store } from 'vuex'

// Declare the extension to @vue/ run-time core package
declare module '@vue/runtime-core' {
  // declare your own store states
  interface State {
    count: number
  }

  // provide typings for `this.$store`
  interface ComponentCustomProperties {
    $store: Store<State>
  }
}
Copy the code

Not the whole story

This is the end of the declaration, but there are still some “module declaration” ways are not covered, because the ultimate goal of this course is vue3 based development does not involve NPM package development, so the rest of the content will not be expanded, if you need to see the TS documentation to learn, with the basis of this article, I’m sure you’ll learn more easily.

WeChat group

Thank you for your reading. If you have any questions, you can add me to the wechat group, and I will pull you into the wechat group (Because Tencent limits the number of wechat groups to 100, when the number exceeds 100, you must be joined by group members).

github

My own open source is based on TS, please visit github.com/any86