theme: cyanosis

Github来源: | 求星星 ✨ | 给个❤️关注,❤️点赞,❤️鼓励一下作者

TypeScript.png

TS基础篇.png

在另一页面打开即可高清

TypeScript.png

TypeScript开发

全局安装typescript,使用安装命令可以使用npm也可以使用yarn

image.png

1
2
3
npm install typescript -g

yarn global add typescript

demo.ts

1
2
3
4
5
6
7
8
9
10
function jeskson() {
let web: string = "hello world"
console.log(web)
}

jeskson()

// tsc
tes demo.ts
node demo.js

image.png

image.png

1
npm install -g ts-node

数据类型

  • TS的数据类型
1
2
3
4
5
6
7
// ES6的数据类型:
基本数据类型:Boolean,Number,String,Symbol,undefined,null

引用类型:Array,Function,Object

// TS的数据类型,增加
void,any,never,元组,枚举,高级类型

类型注解:

1
let hello : string = 'Hello TypeScript'

原始类型

1
2
3
let bl: boolean = true
let num: number = 123
let str: string = "123"

数组泛型

1
2
3
let arrType: Array<number> = [0, 1, 2, 3, 5];
let arrType1: Array<string> = ['0', '1', '2', '3', '5'];
let arrType2: Array<any> = [1, '1', 2, 's', true];

用接口表示数组

1
2
3
4
5
6
7
8
9
10
interface Person{
name: string;
age: number;
}
interface NumberArray {
[index:number]: Person;
}
let arrType3: NumberArray = [{name:'张三',age: 20}]
let arrType4:Array<Person> = [{name:'张三',age: 20}]
let arrType5:Person[] = [{name:'张三',age: 20}]

类数组

类数组(Array-like Object)不是数组类型:

1
2
3
4
5
6
7
8
9
10
function sum() {
let args: number[] = arguments;
}

// index.ts(2,7): error TS2322: Type 'IArguments' is not assignable to type 'number[]'.
// Property 'push' is missing in type 'IArguments'.

function sum() {
let args: IArguments = arguments;
}

元组类型

1
2
3
let tuple: [number, string] = [0, '1']
// 此时,如果改变数组的元素类型或添加元素数量,编辑器都会报错
// TS允许向元组中使用数组的push方法插入新元素(但不允许访问)

函数

函数声明(Function Declaration)和函数表达式(Function Expression)

1
2
3
4
5
6
7
8
9
// 函数声明(Function Declaration)
function sum(x, y) {
return x + y;
}

// 函数表达式(Function Expression)
let mySum = function (x, y) {
return x + y;
};
1
2
3
4
5
6
7
8
9
10
// 函数声明的类型定义
function sum(x:number,y:number):number{
return x+y
}
// 输入多余的或者少于要求的参数,是不被允许的

// 函数表达式
let mySun = function(x:number,y:number):number{
return x + y
}

用接口定义函数的形状

1
2
3
4
5
6
7
8
interface SearchFunc{
(source:string,subString:string):boolean
}

let mySearch:SearchFunc;
mySearch = function(source: string,subString:string){
return source.search(subString) !== -1
}

与接口中的可选属性类似,我们用 ? 表示可选的参数:

1
2
3
4
5
6
7
8
9
function buildName(firstName: string, lastName?: string) {
if (lastName) {
return firstName + ' ' + lastName;
} else {
return firstName;
}
}
let tomcat = buildName('dada', 'Cat');
let tom = buildName('dada');

参数默认值

1
2
3
4
5
function buildName(firstName:string,lastName:string='Cat'){
return firstName + ' ' + lastName;
}
let tomcat = buildName('dada', 'Cat');
let tom = buildName('dada');

剩余参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 可以使用 ...rest 的方式获取函数中的剩余参数

function push(array,...items){
items.forEach(function(item){
array.push(item)
  })
}

let a = [];
push(a,1,2,3)


function push(array:any[],...items:any[]){
items.forEach(function(item){
array.push(item);
})
}

let a = []
push(a,1,2,3)

重载

重载允许一个函数接受不同数量或类型的参数时,作出不同的处理

1
2
3
4
5
6
7
function reverse(x: number | string): number | string {
if (typeof x === 'number') {
return Number(x.toString().split('').reverse().join(''));
} else if (typeof x === 'string') {
return x.split('').reverse().join('');
}
}

使用重载定义多个 reverse 的函数类型:

1
2
3
4
5
6
7
8
9
function reverse(x: number): number;
function reverse(x: string): string;
function reverse(x: number | string): number | string {
if (typeof x === 'number') {
return Number(x.toString().split('').reverse().join(''));
} else if (typeof x === 'string') {
return x.split('').reverse().join('');
}
}

静态类型

1
2
3
4
5
6
7
8
9
10
11
let count : number = 1;

interface dada {
uname: string,
age: number
}

const jeskson :dada = {
uname: 'jeskson',
age: 12
}

对象类型:

1
2
3
4
5
6
7
const gege: {
name: string,
age: number
} = {
name: 'jeskson',
age: 12
}
1
const person : string [] = ['dada', 'jeskson', '掘金魔王哪吒']
1
2
3
4
class Person{}
const dada : Person = new Person()

const dada :()=>string = ()=>{return 'jeskson'}

静态类型:对象类型,数组类型,类类型,函数类型

类型注解与类型推断

局部变量:

1
2
let count : number;
count=12;

如果ts能够自动分析判断变量类型,就不需要,否则就需要使用类型注解。

函数参数和函数的返回类型的注解

1
2
3
4
function getNum(a : number, two : number) : number {
return a + b
}
const total = getNum(1,2)

never

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function errorFunction() : never {
throw new Error()
console.log('hello world')
}

function forNever() : never {
while(true) {}
console.log('hello world')
}

function add({one,two} : {one : number,two : number}) {
return one + two
}
const total = add({one:1,two:2})

数组类型注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const numberArr : number[] = [1,2,3]

const stringArr : string[] = ['a','b','c']

const undefinedArr : undefined[] = [undefined, undefined]

const arr : (number | string)[] = [1,'string',2]

const dada : {name:string,age:number}[] = [
{name:'jeskson',age:12},
{name:'魔王哪吒',age:12},
]

// 类别别名
// type alias
type typeMy = {name:string,age:number}

const dada : typeMy[] = [{name:'jeskson',age:12}]

元组

加强版:

1
2
3
4
const dada : (string | number)[] = ['魔王哪吒','jeskson',12]

// 不常用-元组
const dada1 : [string,string,number] = ["jeskson",12,"dadaqianduan"]

接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
interface dada {
name: 'jeskson';
age: 12;
work ?: string;
say():string;
}

class obj implements dada {
name="dada"
age=12
work="it"
say(){
return "dadaqianduan"
}
}

const selected = (person: dada)=>{
}

// obj.name && console.log(obj.name)

1
2
3
4
5
6
7
8
9
class Da {
content = "掘金魔王哪吒"
sayHello() {
return this.content
}
}

consot da = new Da()
console.log(da.sayHello())
1
2
3
4
5
6
class Person {
name: string;
}
const person = new Person()
person.name = "jeskson"
console.log(person.name)

类的构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Person {
public name : string;
constructor(name:string){
this.name = name
}
}

// 优化
class Person {
constructor(public name:string){}
}
class Teacher extends Person{
constructor(public age:number){
super('jeskson')
}
}

const person = new Person('jeskson')

const dada = new Teacher(12)
console.log(dada.age)
console.log(person.name)

Getter,Setter,static

1
2
3
4
5
6
7
8
9
10
11
class Da {
constructor(private _age:number){}
get age() {
return this._age
}
set age(age:number) {
this._age = age
}
}

const dada = new Da(12)
1
2
3
4
5
6
class Da{
static sayHello() {
return "魔王哪吒"
}
}
console.log(Da.sayHello())

只读属性:

1
2
3
4
5
6
7
8
class Person{
public readonly _name:string
constructor(name:string) {
this._name = name
}
}
const person = new Person('jeskson');
console.log(person.name);

抽象类,使用继承抽象类:

1
2
3
4
5
6
7
8
abstract class Da {
abstract say()
}
class da extends Da {
say() {
console.log('jeskson')
}
}

tsc -init生成tsconfig.json文件:

compilerOptions配置项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
"files": []

removeComments 为 true,去掉注释
strict为true,书写规范

// 允许你的注解类型any不用特意标明
"noImplicitAny": true

// 不允许有null值出现
"strictNullChecks": true

// 入口文件
"rootDir": "./src"
// 编译好的文件
"outDir": "./build"

// Generates corresponding '.map' file
// 信息文件,存储位置信息
"sourceMap": true

// Report errors on unused locals
"noUnusedLocals": true

联合类型和类型保护

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
interface Teacher{
teacher: boolean;
say:()=>{}
}
interface Student{
teacher: boolean;
say:()=>{}
}

//联合类型,类型保护,类型断言
function da(study: Teacher | Student) {
if(study.teacher) {
(study as Teacher).say();
}else{
(study as Student).say();
}
}

泛型

1
2
3
4
function fn<T>(params: Array<T>){
return params;
}
fn<string>(["12","123"]);

使用:

1
2
3
4
5
6
7
8
class Select {
constructor(private da: string[]) {}
getDa(index:number):string{
return this.da[index];
}
}
const dada = new Select(["1","2","3"]);
onsole.log(dada.getDa(1));
1
2
3
4
5
6
class Select<T> {
constructor(private da: T[]){}
getDa(index: number): T{
return this.da[index];
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
interface Girl {
name: string;
}
class SelectGirl<T extends Girl> {
constructor(private girls: T[]) {}
getGirl(index: number): string {
return this.girls[index].name;
}
}

class SelectGirl<T extends number | string> {
constructor(private girls: T[]) {}
getGirl(index: number): T {
return this.girls[index];
}
}

NameSpace

npm init -y生成package.json文件

tsc -init生成tsconfig.json文件

安装VsCode编辑器:

image.png

1
2
3
4
5
6
interface Person {
name: string
}
const teacher: Person = {
name: 'jeskson'
}

基础类型和对象类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 基础类型 null, undefined, symbol, boolean, void
const count:number = 12;
const name:string = '掘金魔王哪吒';

// 对象类型
const teacher: {
name: string;
age: number;
} = {
name: 'jeskson',
age: 12
};

const nums:number[] = [1,2,3]

const goTotal: ()=>number = () => {
return 123;
}

类型注解和类型推断

1
2
3
4
5
6
// type annotation 类型注解

let count:number;
count=123;

// type inference 类型推断,TS会自动的尝试分析变量的类型
1
2
3
4
5
// 推动不出来,就自己加
function getTotal(firstNumber:number, secondNumber:number) {
return firstNumber + secondNumber;
}
const total = getTotal(1,2);

函数相关类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 实战
function getTotal(firstNumber:number, secondNumber:number):number {
return firstNumber + secondNumber;
}
const total = getTotal(1,2);

// void这个函数不应该有返回值
function sayHello(): void {
console.log('hello');
}

// never 表示这个函数永远不能执行完成
function errorEmitter(): never {
while(true){} // 或抛出异常
}
1
2
3
4
5
6
function add({first,second}:{first:number;second:number}):number{
return first+second;
}
function getNumber({first}:{first:number}){
return first;
}

小结:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 基础类型 boolean,number,string,void,undefined,symbol,null

let count: number;
count = 12;

// 对象类型 {},Class,function,[]
const fun = (str:string) => {
return parseInt(str,10);
}
const fun1: (str:string)=>number = (str) => {
return parseInt(str,10);
}

const date = new Date();

数组和元组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
const arr: (number|string)[] = [1,'2',3];
const stringArr: string[] = ['a','b','c'];
const undefinedArr:undefined[] = [undefined];

const objectArr: {name:string,age:number}[] = [{
name: '掘金魔王哪吒',
age: 12
}]

// type alias 类型别名
type User = {name:string;age:number};
const objectArr: User[] = [{
name: '掘金魔王哪吒',
age: 12
}]

class Teacher {
name: string;
age: number;
}
const objectArr: Teacher[] = [
new Teacher();
{
name: 'jeskson',
age: 12
}
];

元组

1
const teacherInfo: [string, string, number] = ['dadaqianduan','1024bibi.com',12];

Interface接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
interface Person {
// readonly name: string;
name: string;
age?: number;
}
const getPersonName = (person: Person): void => {
console.log(person.name);
};
const setPersonName = (person: Person, name: string): void=>{
persono.name = name;
};
const person = {
name: '掘金魔王哪吒',
age: 12
};
getPersonName(person);

类的定义与继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Person {
name='掘金魔王哪吒';
getName() {
return this.name;
}
}
class Teacher textends Person {
getTeacherName() {
return 'teacher';
}
getName() {
return '1024bibi.com' + super.getName()
}
}

const teacher = new Teacher();
// 重写,字类可以重写父类的东西

类中的访问类型和构造器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// private protected public 
class Person {
public name: string;
sayHi() {
console.log('1024bibi.com')
}
}
const person = new Person();
person.name = '掘金魔王哪吒'
console.log(person.name);

// public 允许我在类的内外被调用
// private 允许在类内被使用
// protected 允许在类内以及继承的子类中是使用
  • constructor
1
2
3
4
5
6
7
8
9
class Person {
public name: string;
constructor(name: string) {
this.name = name;
}
}

const person = new Person('dadaqianduan');
console.log(person.name);
1
2
3
4
// 简化写法
class Person {
constructor(public name: string) {}
}
1
2
3
4
5
6
7
class Teacher extends Person {
constructor(public age:number) {
super('dadaqianduan');
}
}

// 如果父类没有构造器,也使用空的 super()

静态属性,Setter和Getter

1
2
3
4
5
6
7
8
9
class Person {
constructor(private name: string) {}
get getName() {
return this.name;
}
}

const person = new Person('dadaqianduan');
console.log(person.getName);
1
2
3
4
5
6
7
8
9
class Person {
constructor(private _name: string) {}
get name() {
return this._name;
}
set name(name: string) {
this._name = name;
}
}

设计模式:单例模式,一个类只允许通过这个类,获取一个单例实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Demo {
private static instance: Demo;
private constructor(public name:string) {}

static getInstance(name: string) {
if(!this.instance) {
this.instance = new Demo('1024bibi.com');
}
return this.instance;
}
}

//const demo1 = new Demo();
//const demo2 = new Demo();

const demo1 = Demo.getInstance();

抽象类

抽象类只能被继承,不能被实例化

1
2
3
4
5
6
7
abstract class Da {
width: number;
getType() {
return 'dadaqianduan';
}
abstract getAra(): number;
}
1
npm init -y

生成package.json文件:

1
2
3
4
5
6
7
8
9
10
11
12
{
"name": "TypeScript",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test: "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
1
2
3
4
tsc --init
// Successfully created a tsconfig.json file

// npm uninstall ts-node -g
1
npm install -D ts-node
1
npm install typescript -D

TypeScript中的配置文件

1
2
3
4
// 编译配置文件
// tsconfig.json
要编译的文件
"include" ["./demo.ts"],

联合类型和类型保护

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
interface Bird {
fly: boolean;
sing: ()=>{};
}
interface Dog {
fly: boolean;
bark: ()=>{};
}
// 类型断言的方式
function trainAnial(animal: Bird | Dog) {
if(animal.fly) {
(animal as Bird).sing();
} else {
(animal as Dog).bark();
}
}

// in 语法来做类型保护
function trainAnialSecond(animal: Bird | Dog) {
if('sing' in animal) {
animal.sing();
} else {
animal.bark();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// typeof 语法来做类型保护
function add(first: string | number, second: string | number) {
if(typeof first === 'string' || typeof second === 'string') {
return `${first}${second}`;
}
return first + second;
}

// 使用instanceof语法来做类型保护
class NumberObj {
count: number;
}

function addSecond(first: object | NumberObj, second: object | NumberObj) {
if(first instanceof NumberObj && second instanceof NumberObj) {
return first.count + second.count;
}
return 0;
}

Enum枚举类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const Status = {
OFFLINE: 0,
ONLINE: 1,
DELETED: 2
}
function getResult(status) {
if(status === Status.OFFLINE){
return 'offline';
}else if(status === Status.ONLINE) {
return 'online';
}else if(status === Status.DELETED) {
return 'deleted';
}
return 'error';
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
enum Status {
OFFLINE,
ONLINE,
DELETED2
}
function getResult(status) {
if(status === Status.OFFLINE){
return 'offline';
}else if(status === Status.ONLINE) {
return 'online';
}else if(status === Status.DELETED) {
return 'deleted';
}
return 'error';
}

函数泛型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 泛型generic泛指的 类型
function join<T,P>(first: T, second: P) {
return `${first}${second}`;
}
function anotherJoin<T>(first: T,second: T): T {
return first;
}

// T[]
function map<T>(params: Array<T>) {
return params;
}
// join<number,string>(1,'1');
// map<string>(['123']);
join(1,'1');

类中如何使用泛型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
interface Item {
name: string;
}
class DataManager<T extends Item> {
constructor(private data: T[]) {}
getItem(index: number):string {
return this.data[index].name;
}
}

const data = new DataManager({
{
name: 'jeskson'
}
]};

// 用泛型可以声明一些类型:
// 如何使用泛型作为一个具体的类型注解
function hello<T>(params: T) {
return params;
}
const func: <T>(param: T) => T = hello;

命名空间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
"use strict"
var Header = (function() {
function Header() {
var elem = document.createElement('div');
elem.innerText = 'This is Header';
document.body.appendChild(elem);
}
return Header;
}());

var Content = (function()=>{
function Content() {
var elem = document.createElement('div');
elem.innerText = 'This is Content';
document.body.appendChild(elem);
}
return Content
}());

使用Parcel打包TS代码

1
yarn add --dev parcel@next

泛型中keyof语法的使用

某一数据类型的key的数组集合,既适用于数组,也适用于对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
interface testInter {
name: string,
age: number
}
let testArr: string[] = ['dada', 'dada1'];
let testObj: testInter = {name: 'tate', age: 26}

// 数组
function showKey<K extends keyof T, T> (key: K, obj: Array<string>) {
return key;
}
showKey<number, Array<string>>(1, testArr);

// 对象
function showKey<K extends keyof T, T> (keyItem: K, obj: T): K {
return keyItem;
}
let val = showKey('name', testObj)

function showKey<K extends keyof T, T> (items: K[], obj: T): T[K][] {
return items.map(item => obj[item])
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
interface Person {
name: string;
age: number;
gender: string;
}
class Teacher {
constructor(private info: Person) {}
getInfo(key: string) {
if(key==='name' || key==='age' || key==='gender') {
return this.info[key];
}
}
}

const teacher = new Teacher({
name: 'jeskson',
age: 12,
gender: 'male'
});
const test = teacher.genInfo('name');
1
2
3
4
5
6
7
class Teacher {
constructor(private info: Person) {}
// getInfo<T extends keyof Person>(key:string) {
getInfo<T extends keyof Person>(key: T):Person[T]{
return this.info[key];
}
}

装饰器

1
2
3
// 类的装饰器
// 装饰器本身是一个函数
// 装饰器通过@符号来使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 普通方法,target对应的是类的prototype
// 静态方法,target对应的是类的构造函数
function getNameDecorator(target:any,key:string){
console.log(target,key);
}
class Test {
name: string;
constructor(name: string){
this.name = name;
}
@getNameDecorator
static getName() {
return '123';
}
}

接口Interface

有时候我们传入的参数可能会包含很多的属性,但编译器只会检查那些必须的属性是否存在,以及类型是否匹配,而接口就是用来描述这样的结构。

1
2
3
4
5
6
7
8
9
10
11
12
13
function Person(config: {name:string,age:number}) {
console.log(config.name+config.age);
}
console.log(Person({name:'魔王哪吒',age:12}));
// 重构
interface Config {
name: string;
age: number;
}
function Person(config: Config) {
console.log(config.name+config.age);
}
// 接口类型检查会检测属性有没有在Config接口中而进行限制

可选属性

接口中的属性有时候是不必须的,有的用得到,有的用不到的情况下,是可选属性,这样对可能存在的属性进行预先定义。

1
2
3
4
5
6
7
8
interface Config {
name: string;
age?: number;
// [propName: string]: any 转字符串索引签名
}
// [propName: string]: any
// 这个索引签名是为了你能够预见某个对象可能有某些特殊的用途而准备的
// 属性名写错,可以通过索引签名的方式进行屏蔽错误

只读属性

对于一些对象属性只能在对象刚刚创建的时候修改其值,在属性前用readonly来指定只读属性:

1
2
3
4
5
6
interface Point {
readonly x: number;
readonly y: number;
}
let p:Point = { x: 12, y: 14 }
p.x = 15 // 错误

函数类型

接口能够描述JavaScript中对象拥有的各种各样的外形

函数类型接口:

1
2
3
interface Fun {
(source: string, subString: string): Boolean
}

接口继承

接口是可以相互继承的,能够从一个接口里复制成员到另一个接口里。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
interface Animal {
name: string;
say(): void;
}
interface Person extends Animal {
work(): void;
closer: string;
}
class Pro implements Person {
closer: string;
name: string;
say(): void {

}
work(): void {

}
constructor(name:string, closer:string) {
this.name = name;
this.closer = closer;
}
}
let g:Person = new Pro("jeskson","it");
g.say();
g.work();
  • 对象类型接口
  • 函数类型接口

接口的定义方式:使用interface关键字

接口中可定义:

  • 确定属性
  • 可选属性
  • 任意属性
  • 只读属性
  1. 确定属性
1
2
3
4
5
6
7
8
9
interface UserInfo {
name: string;
age: number;
}

const myInfo: UserInfo = {
name: '魔王哪吒',
age: 12
}

接口中约束好的确定属性,定义对象变量的时候,不能少,也不能多🙅‍

  1. 可选属性
1
2
3
4
5
6
7
8
9
10
interface UserInfo {
name: string;
age: number;
sex?: string;
}

const myInfo: UserInfo = {
name: '魔王哪吒',
age: 12
}

接口中的可选属性,是表示在对象变量中可以不存在

  1. 任意属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
interface UserInfo {
name: string;
age: number;
sex?: string;
[proName: string]: any;
}

const myInfo: UserInfo = {
name: "dadaqianduan",
age: 12,
test1: '123',
test2: 'abc',
test3: 123
};

一旦定义了任意属性,那么确定属性和可选属性的类型都必须是任意属性类型的子类,定义了任意属性后,对象变量中的属性个数才可以出现比接口的属性数量多的情况。

  1. 只读属性
1
2
3
4
5
6
7
interface UserInfo {
readonly id: number;
name: string;
age: number;
sex?: string;
[propName: string]: any;
}
1
2
3
4
5
6
7
8
const myInfo: UserInfo = {
id: 1,
name: "dada",
age: 12,
test1: "123",
test2: "abc",
test3: 123
};

只读属性,也是确定属性,在对象变量定义的时候必须有值,后面不能修改

  • 对象接口,以查询商品列表接口API示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
interface ResponseData {
resCode: number;
resData: ResultData[];
message: string;
}

interface ResultData {
productId: number;
productName: string;
}

let resultData = {
resCode: 0,
resData: [
{ productId: 1, productName:"TypeScipt实战" },
{ productId: 2, productName:"TypeScipt从入门到精通" },
],
message: "success"
}

function render(res: ResponseData) {
console.log(res.resCode, res.message)
res.resData.forEach((obj) => {
console.log(obj.productId, obj.productName)
})
}

render(resultData);

只要传入的对象满足接口的必要条件就可以被允许,即使传入多余的字段也可以通过类型检查

  • 绕过检查的方法有3种:
  1. 将对象字面量赋值给一个变量
1
2
3
4
5
6
7
8
9
let result = {
resCode: 0,
resData: [
{ productId: 1, productName:"TypeScipt实战", remark: "备注"},
{ productId: 2, productName:"TypeScipt从入门到精通" },
],
message: "success"
}
render(result)
  1. 使用类型断言

使用类型断言方式,明确告诉编译器类型是什么,编译器就会绕过类型检查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
render({
resCode: 0,
resData: [
{ productId: 1, productName:"TypeScipt实战", remark:""},
{ productId: 2, productName:"TypeScipt从入门到精通" },
],
message: "success"
} as ResponseData)

render(<ResponseData>{
resCode: 0,
resData: [
{ productId: 1, productName:"TypeScipt实战", remark: "备注"},
{ productId: 2, productName:"TypeScipt从入门到精通" },
],
message: "success"
})
  1. 使用字符串索引签名
1
2
3
4
5
interface ResultData {
productId: number;
productName: string;
[remark: string]: any; // 字符串索引签名
}

函数接口

  • 函数定义方式:
  1. 在TS中,使用一个变量直接定义函数
1
2
3
4
5
let add: (x: number, y: number) => number
= (x, y){
return x+y;
};
add(1,2)
  1. 使用接口定义函数
1
2
3
4
5
6
7
interface Add {
(x: number, y: number): number
}
let myFunc: Add = function(x, y){
return x+y;
};
myFunc(1,2);
  1. 使用类型别名来定义函数

类型别名使用type关键字

1
type Add = (x: number, y: number) => number

可索引类型的接口

1
2
3
4
5
6
// 数字索引接口
interface numberIndex {
[x: number]: string
}
// 相当于声明了一个字符串类型的数组
let chars: numberIndex = ['A', 'B']
1
2
3
4
// 声明一个字符串索引类型的接口
interface stringIndex {
[x: string]: string
}
1
2
3
4
5
6
7
8
9
10
// 两种索引签名混用
interface stringIndex {
[x: string]: string
[z: number]: number // // Numeric index type 'number' is not assignable to string index type 'string'.
}

interface stringIndex {
[x: string]: any
[z: number]: number // Numeric index type 'number' is not assignable to string index type 'string'.
}

上手TypeScipt

对于npm的用户

1
npm install -g typescript

构建第一个TypeScript文件,dada.ts 文件:

1
2
3
4
5
function dada(person) {
return "hello" + person;
}
let user = "jeskson";
document.body.innerHTML = dada(uer);

编译代码

在命令行上,运行TypeScript编译器:

1
tsc dada.ts

添加类型注解: string

1
2
3
4
5
function dada(person: string) {
return "jeskson"+person;
}
let user = "jeskson";
document.body.innerHTML = dada(user);

类型注解

TypeScript里的类型注解是一种轻量级的为函数或变量添加约束的方式。

接口

允许我们在实现接口的时候只要保证包含了接口要求的结构就可以

1
2
3
4
5
6
7
8
9
10
11
12
13
// implements语句
interface Person {
firstName: string;
lastName: string;
}

function func(peson: Person) {
return person.firstName + person.lastName;
}

let user = { firstName: "jeskson", lastName: "User" };

document.body.innerHTML = func(user);

类,支持基于类的面向对象编程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Student {
fullName: string;
constructor(public firstName: string, public lastName: string) {
this.fullName = firstName + lastName;
}
}

interface Person {
firstName: string;
lastName: string;
}

function dada(person: Person) {
return person.firstName+person.lastName;
}

let user = new Student("jeskson","魔王哪吒");
document.body.innerHTML = dada(user);

运行TypeScript Web应用

index.html里输入内容:

1
2
3
4
5
6
7
<!DOCTYPE html>
<html>
<head><title>TypeScript dada</title></head>
<body>
<script src="dada.js"></script>
</body>
</html>

对象

在JS中,可以任意修改对象属性,TS中不允许

1
2
3
// 这是因为,仅声明了对象obj的类型注解是object
let obj: object = {x: 'a', y: 'b'}
obj.x = 3 // Property 'x' does not exist on type 'object'.
1
2
let obj: {x: string, y: string} = {x: 'a', y: 'b'}
obj.x = 'c'

Symbol

具有唯一的值,可以显式声明,也可直接创建

1
2
3
4
5
let symbol1: Symbol = Symbol()  // 显示声明
let symbol2 = Symbol() // 直接创建

// 验证:是否是同一个对象
console.log(symbol1 === symbol2) // fasle

undefined 和 null

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 一旦声明了undefined,就不能再被赋值为任何其他的数据类型了
let udf: undefined = undefined
let nu: null = null

let undf: undefined = 1
// Type '1' is not assignable to type 'undefined'.

// 默认情况下,undefined和null也不能被赋值给任何其他类型

let num1: number = undefined
// Type 'undefined' is not assignable to type 'number'.

let num2: number = null
// Type 'null' is not assignable to type 'number'.
  • 在TS中,undefined和null是任何类型的子类型,所以可以被赋值给其他类型
  • 设置允许被赋值为其他类型

打开tsconfig.js,将strictNullChecks = false(默认true)

void,any,never

  • js中,void操作符可以使任何一个表达式返回undefined
  • void 0 // 将返回undefined
1
2
// void
let voidFunc = () => {}
  • any:如果不指定TS的变量类型,默认为any类型,可以赋值为任何类型
  • never:永远不会有返回值的类型
1
2
3
4
5
6
7
8
9
// 函数抛出异常,永远不会有返回值,类型为never
let error = () => {
throw new Error('error')
}

// 死循环函数永远没有返回值,类型为never
let endless = () => {
while(true) {}
}

对数组中的对象按对象的值进行去重

1
2
3
let listData = [
{ firstName: "dada", lastName: "abc", size: 18 }
}
1
2
3
4
5
6
7
8
9
//js
let obj = {};
listData = listData.reduce((item, next) => {
if (!obj[next.lastName]) {
item.push(next);
obj[next.lastName] = true;
}
return item;
}, []);
1
2
3
4
5
6
7
8
9
//ts
const obj: {[key: string]: boolean;} = {};
listData = listData.reduce<ListDataItem[]>((item, next) => {
if (!obj[next.lastName]) {
item.push(next);
obj[next.lastName] = true;
}
return item;
},[]);

在微信小程序开发中使用Typescript

image.png

image.png

image.png

image.png

模块的概念

“内部模块”现在称为“命令空间”,“外部模块”现在简称为“模块”,模块字其自身的作用域里执行,而不是在全局作用域里。

这意味着定义在一个模块里的变量,函数,类等等在模块外部是不可见的,除非你明确地使用export形式之一导出它们。

相反,如果想使用其它模块导出的变量,函数,类,接口等的时候,你必须要导入它们,可以使用 import 形式之一。

模块的概念:

我们可以把一些公共的功能单独抽离成一个文件作为一个模块,模块里面的变量,函数,类等默认是私有的,如果我们要在外部访问模块里面的数据,我们需要通过export暴露模块里面的数据。暴露后使用import引入模块就可以使用模块里面暴露的数据。

命名空间

命名空间和模块的区别

  • 命名空间:内部模块,主要用于组织代码,避免命名冲突
  • 模块:ts的外部模块的简称
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
namespace A {
interface Animal {
name: string;
eat(): void;
}
class Dog implements Animal {
name: string;
constructor(theName: string) {
this.name = theName;
}
eat() {
console.log('dog');
}
}
class Cat implements Animal {
name: string;
constructor(theName: string) {
this.name = theName;
}
eat() {
console.log('cat');
}
}
let dog = new Dog('dogdog');
dog.eat();
}
1
2
3
4
import {A,B} from './modules/animal';

var dog = new A.Dog('hei');
dog.eat();

装饰器

装饰器是一种特殊类型的声明,它能够被附加到类声明,方法,属性或参数上,可以修改类的行为。

通俗的讲装饰器就是一个方法,可以注入到类,方法,属性参数上扩展类,属性,方法,参数的功能。

常见的装饰器有:类装饰器,属性装饰器,方法装饰器,参数装饰器

装饰器的写法:

  • 普通装饰器(无法传参)
  • 装饰器工厂(可传参)

方法参数装饰器:

参数装饰器表达式会在运行时当作函数被调用,可以使用参数装饰器为类的原型增加一些元素数据,传入下列3个参数:

  • 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
  • 方法的名字
  • 参数在函数参数列表中的索引
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function logParams(params:any){
return function(target:any,methodName:any,paramsIndex:any){
console.log(params);
console.log(target);
console.log(methodName);
console.log(paramsIndex);
}
}
class HttpClient{
public url:any|undefined;
constructor(){}
getDate(@logParams('xxx') uuid:any){
console.log(uuid);
}
}

keyof

1
2
3
4
5
6
7
keyof与Object.keys相似,keyof取interface的键
interface Point {
x: number;
y: number;
}
// type keys = "x" | "y"
type keys = keyof Point;
1
2
3
4
5
6
7
8
9
10
// 无法确认返回类型
// 无法对 key 做约束
const data = {
a: 1,
b: 2
}

function get(o: object, name: string) {
return o[name]
}

使用keyof:

1
2
3
function get<T extends object, K extends keyof T>(o: T, name: K): T[K] {
return o[name]
}

?: 运算符

1
2
3
T extends U ? X : Y

type isTrue<T> = T extends true ? true : false

tsconfig.json

tsconfig.json文件中指定了用来编译这个项目的根文件和编译选项

tsconfig.json示例文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//"compilerOptions"可以被忽略,这时编译器会使用默认值。
//使用"files"属性
//"files"指定一个包含相对或绝对文件路径的列表。
{
"compilerOptions": {
"module": "commonjs",
"noImplicitAny": true,
"removeComments": true,
"preserveConstEnums": true,
"sourceMap": true
},
"files": [
"core.ts",
"sys.ts",
"types.ts",
"scanner.ts",
"parser.ts",
"utilities.ts",
"binder.ts",
"checker.ts",
"emitter.ts",
"program.ts",
"commandLineParser.ts",
"tsc.ts",
"diagnosticInformationMap.generated.ts"
]
}
//使用"include"和"exclude"属性
//如果"files"和"include"都没有被指定,编译器默认包含当前目录和子目录下所有的TypeScript文件
//排除在"exclude"里指定的文件
{
"compilerOptions": {
"module": "system",
"noImplicitAny": true,
"removeComments": true,
"preserveConstEnums": true,
"outFile": "../../built/local/tsc.js",
"sourceMap": true
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules",
"**/*.spec.ts"
]
}

查看模式: http://json.schemastore.org/tsconfig.

回看笔者往期高赞文章,也许能收获更多喔!

❤️关注+点赞+收藏+评论+转发❤️

点赞、收藏和评论

我是Jeskson(达达前端),感谢各位人才的:点赞、收藏和评论,我们下期见!(如本文内容有地方讲解有误,欢迎指出☞谢谢,一起学习了)

我们下期见!

文章持续更新,可以微信搜一搜「 程序员哆啦A梦 」第一时间阅读,回复【资料】有我准备的一线大厂资料,本文 https://www.1024bibi.com 已经收录

github收录,欢迎Starhttps://github.com/webVueBlog/WebFamily