安装

npm install -g typescript

安装完成,检测:tsc -V

VScode自动编译

1). 生成配置文件tsconfig.jsontsc --init
2). 修改tsconfig.json配置"outDir": "./js","strict": false,
3). 启动监视任务: 终端 -> 运行任务 -> 监视tsconfig.json

类型

function greeter (person: string) {return 'Hello, ' + person
}let user = 'Yee'console.log(greeter(user))
  1. 如果类型错误或者缺少参数ts会直接报错,但是任然可以创建对应的js文件,只是会警告代码可能不会按预期执行

接口

让我们继续扩展这个示例应用。这里我们使用接口来描述一个拥有 firstNamelastName 字段的对象。 在 TypeScript 里,只在两个类型内部的结构兼容,那么这两个类型就是兼容的。 这就允许我们在实现接口时候只要保证包含了接口要求的结构就可以,而不必明确地使用 implements 语句。

  1. 在 TypeScript 中,我们使用接口(Interfaces)来定义对象的类型。

    interface Person {name:string;
    age:number;
    }
    let tom:Person={name:‘Tom’,
    age:25
    };
    
interface Person {firstName: stringlastName: string
}function greeter (person: Person) {return 'Hello, ' + person.firstName + ' ' + person.lastName
}let user = {firstName: 'Yee',lastName: 'Huang'
}console.log(greeter(user))

// 定义一个接口
interface IPerson {firstname: string, // 姓氏lastname: string // 名字
}// 定义一个类
class Person {firstname: string // 姓氏lastname: string // 名字fullname: string // 姓名// 定义一个构造器constructor(firstname: string, lastname: string) {// 更新属性数据this.firstname = firstnamethis.lastname = lastnamethis.fullname = this.firstname + this.lastname}
}// 定义一个函数
function showFullName(person: IPerson) {return person.firstname + person.lastname
}// 实例化对象
const person_1 = new Person('诸葛', '明亮')console.log(showFullName(person_1)); // 诸葛明亮

使用webpack打包TS

  1. 创建public => index.html文件

  2. 创建build => webpack.config.js文件

    const { CleanWebpackPlugin } = require('clean-webpack-plugin')
    const HtmlWebpackPlugin = require('html-webpack-plugin')
    const path = require('path')const isProd = process.env.NODE_ENV === 'production' // 是否生产环境function resolve(dir) {return path.resolve(__dirname, '..', dir)
    }module.exports = {mode: isProd ? 'production' : 'development', // 生产模式entry: {app: './src/main.ts' // 程序的主入口目录},// 打包output: {path: resolve('dist'), // 打包后文件放在dist目录filename: '[name].[contenthash:8].js' // 并以app.八位哈希值.js的方式命名},module: {rules: [// 针对src目录下的ts、tsx文件操作{test: /\.tsx?$/,use: 'ts-loader',include: [resolve('src')]}]},plugins: [// 把dist目录中以前打的包清除掉new CleanWebpackPlugin({}),// 针对public目录下的index.html进行打包new HtmlWebpackPlugin({template: './public/index.html'})],// 引入这几种文件可以不写他的扩展名resolve: {extensions: ['.ts', '.tsx', '.js']},// 代码出现错误后的提示信息devtool: isProd ? 'cheap-module-source-map' : 'cheap-module-eval-source-map',// 使用localhost这个主机名在8081端口打开devServer: {host: 'localhost', // 主机名stats: 'errors-only', // 打包日志输出输出错误信息port: 8081,open: true},
    }
    
  3. 创建src => main.ts 文件

  4. 输入 npm init -y

  5. 输入tsc --init

  6. 下载依赖

    npm install -D typescript
    npm install -D webpack@4.41.5 webpack-cli@3.3.10 webpack-dev-server@3.10.2
    npm install -D html-webpack-plugin clean-webpack-plugin ts-loader cross-env
    
    "clean-webpack-plugin": "^3.0.0",
    "cross-env": "^7.0.2",
    "html-webpack-plugin": "^4.5.0",
    "ts-loader": "^8.0.11",
    "typescript": "^4.0.5",
    "webpack": "^4.41.5",
    "webpack-cli": "^3.3.10",
    "webpack-dev-server": "^3.10.2"
    
  7. 配置打包命令

    "dev": "cross-env NODE_ENV=development webpack-dev-server --config build/webpack.config.js",
    "build": "cross-env NODE_ENV=production webpack --config build/webpack.config.js"
  8. 运行与打包

    npm run dev
    npm run build

基础类型

TypeScript 支持与 JavaScript 几乎相同的数据类型,此外还提供了实用的枚举类型方便我们使用。

总结:在TS中变量一开始是什么类型,那么后期赋值就只能用什么类型,是不允许其他类型的数据赋值给当前变量的

// 基本语法
// let 变量名 : 数据类型 = 值

布尔类型

最基本的数据类型就是简单的 true/false 值,在JavaScript 和 TypeScript 里叫做 boolean(其它语言中也一样)。

let flag: boolean = true
console.log(flag); // true

数字类型

和 JavaScript 一样,TypeScript 里的所有数字都是浮点数。 这些浮点数的类型是 number。 除了支持十进制和十六进制字面量,TypeScript 还支持 ECMAScript 2015中引入的二进制和八进制字面量。

let a1: number = 10 // 十进制
let a2: number = 0b1010  // 二进制
let a3: number = 0o12 // 八进制
let a4: number = 0xa // 十六进制

字符串类型

JavaScript 程序的另一项基本操作是处理网页或服务器端的文本数据。 像其它语言里一样,我们使用 string 表示文本数据类型。 和 JavaScript 一样,可以使用双引号(")或单引号(')表示字符串。

let str1: string = '床前明月光'
let str2: string = '疑是地上霜'
let str3: string = '举头望明月'
let str4: string = '低头思故乡'
console.log(`${str1},${str2},${str3},${str4}`); // 床前明月光,疑是地上霜,举头望明月,低头思故乡

字符串类型 + 数字类型

let str: string = '我有多少钱?'
let num: number = 100
console.log(str + num); // 我有多少钱?100

undefined与null

// null与undefind
let und: undefined = undefined
let nul: null = null
console.log(und, nul);

undefined和null可以作为其他类型的子类型,把undefined和null赋值给其他类型的变量,比如:number类型的变量

// 注意需要关闭严格模式
// "strict": false,       /* 严格模式 */
let num2: number = undefined
console.log(num2);
let num3: number = null
console.log(num3);

数组类型

  1. 数组定义方式一

    语法:let 属性名:数据类型[] = [值1,值2,值3]

  2. 数组定义方式二

    语法:let 属性名:Array<数据类型> = [值1,值2,值3]

  3. 注意问题: 数组定义后,里面数据的类型必须和定义数组时的类型相同,否则有错误提示信息,也不会编译通过的

  // 数组定义方式一// 语法:let 属性名:数据类型[] = [值1,值2,值3]let arr1: number[] = [1, 2, 3]// 数组定义方式二// 语法:let 属性名:Array<数据类型> = [值1,值2,值3]let arr2: Array<number> = [1, 2, 3]console.log(arr1);console.log(arr2);// arr2=['',1,true] // 报错:字符串不能赋值给number类型   报错:布尔值不能赋值给number类型// console.log(arr2); // 会报错,但还是可以输出['',1,true]

元组类型

  1. 语法:let 属性名:[数据类型1,数据类型2] = [满足数据类型1的值,满足数据类型2的值]
  2. 注意问题:元组类型在使用的时候,数据类型的位置和个数,应该和定义元组时的数据类型和位置对应上
  let arr3: [string, number, boolean] = ['我的压岁钱', 100, true] // 数组值的个数可以超过,可以可以少于console.log(arr3); // ['我的压岁钱', 100, true]// arr3 = [100, '我的压岁钱', true], // 报错:类型错误,把不该赋值的类型付给了他们// console.log(arr3); // [100, '我的压岁钱', true]

枚举类型

  1. 枚举里面的每个数据都可以叫做元素,每个元素有自己的编号,编号是从0开始的,一次递增加1.编号也可与自己定义

    enum Color {red,blue,yellow,green = 100
    }
    let color: Color = Color.red
    console.log(color); // 0
    console.log(Color.red, Color.blue, Color.yellow, Color.green); // 0,1,2,100
  2. // 枚举类型提供的一个便利是你可以由枚举的值得到它的名字。

    enum gender {男,女
    }
    console.log(gender.女); // 1

    any类型

  3. 可以存储任何类型

    有时候,我们会想要为那些在编程阶段还不清楚类型的变量指定一个类型。 这些值可能来自于动态的内容,比如来自用户输入或第三方代码库。 这种情况下,我们不希望类型检查器对这些值进行检查而是直接让它们通过编译阶段的检查。 那么我们可以使用 any 类型来标记这些变量:

    let arr: any[] = [100, '提莫队长,正在待命', true]
    console.log(arr);
  4. 缺点,当方法使用不当时虽然编译不会报错,但是在浏览器上实际显示是报错的

    console.log(arr[0].split(''));

void类型

  1. 某种程度上来说,void 类型像是与 any 类型相反,它表示没有任何类型。 当一个函数没有返回值时,你通常会见到其返回值类型是 void:

    function showMsg(): void {console.log('床前明月光');// return '你好'  // 报错,string类型的值不能赋值给void// return null // underfind和void类型可以赋给他
    }
    showMsg() // 床前明月光
    console.log(showMsg()); // underfind

object类型

  1. object 表示非原始类型,也就是除 numberstringboolean之外的类型。
// 定义一个函数,参数是object类型,返回值也是object类型
function getObj(obj: object): object {console.log(obj);return {name: "苏轼"}
}
// console.log(getObj('苏辙')); // 报错:参数类型不对
console.log(getObj({ name: '苏辙' }));  // {name: '苏辙'} | {name: '苏轼'}
console.log(getObj(new String('苏洵'))); // String {'苏洵'} | {name: '苏轼'}
console.log(getObj(String)); // ƒ String() { [native code] } | {name: '苏轼'}

联合类型

  1. 联合类型(Union Types)表示取值可以为多种类型中的一种

    function user(sex: string | number): string {console.log(sex);console.log(sex.toString().length);let arr: Array<string | number> = ['1', 2]console.log(arr);return '大家好'
    }
    console.log(user('男')); // 男 | 1 | 大家好 | ['1', 2]
    console.log(user(1)); // 1 | 1 | 大家好 | ['1', 2]

类型断言

  1. 通过类型断言这种方式可以告诉编译器,“相信我,我知道自己在干什么”。 类型断言好比其它语言里的类型转换,但是不进行特殊的数据检查和解构。 它没有运行时的影响,只是在编译阶段起作用。 TypeScript 会假设你,程序员,已经进行了必须的检查。

  2. 以<>的形式

    function user1(sex: string | number): number {if ((<string>sex).length) {return (<string>sex).length} else {return (<number>sex).toString().length}
    }
    console.log(user1('12345')); // 5
    console.log(user1(234)); // 3
  3. 以as的形式

    function user2(sex: string | number): number {if ((sex as string).length) {return (sex as string).length} else {return (sex as number).toString().length}
    }
    console.log(user2('12345')); // 5
    console.log(user2(234)); // 3

类型推断

  1. 类型推断: TS会在没有明确的指定类型的时候推测出一个类型

  2. 有下面2种情况: 1. 定义变量时赋值了, 推断为对应的类型. 2. 定义变量时没有赋值, 推断为any类型

  3. /* 定义变量时赋值了, 推断为对应的类型 */
    let b9 = 123 // number
    // b9 = 'abc' // error/* 定义变量时没有赋值, 推断为any类型 */
    let b10  // any类型
    b10 = 123
    b10 = 'abc'

接口

TypeScript 的核心原则之一是对值所具有的结构进行类型检查。我们使用接口(Interfaces)来定义对象的类型。接口是对象的状态(属性)和行为(方法)的抽象(描述)

  1. 接口用interface表示

  2. 只读属性:readonly

    1. 一些对象属性只能在对象刚刚创建的时候修改其值。 你可以在属性名前用 readonly 来指定只读属性
    2. 一旦赋值后再也不能被改变了。
    3. 最简单判断该用 readonly 还是 const 的方法是看要把它做为变量使用还是做为一个属性。 做为变量使用的话用 const,若做为属性则使用 readonly
  3. 可选属性:?

    1. 可选属性的好处之一是可以对可能存在的属性进行预定义,好处之二是可以捕获引用了不存在的属性时的错误。
     // 需求: 创建人的对象, 需要对人的属性进行一定的约束// id是number类型, 必须有, 只读的// name是string类型, 必须有// age是number类型, 必须有// sex是string类型, 可以没有// 定义一个接口,readonly:只读的  ?:可有可无的interface IPerson {readonly id: number,name: string,sex?: string,age: number}// 定义一个对象,该对象的类型是接口IPersonconst person: IPerson = {id: 1,name: '张三',age: 18,// sex: '男'}console.log(person);    // {id: 1, name: '张三', age: 18}

函数类型

  1. 通过接口的形式来作为函数的类型

  2. 为了使接口表示函数类型,我们需要给接口定义一个调用签名

  3. 他就像是一个只有参数列表和返回值类型的函数定义。参数列表中的每一个参数都有自己的属性和姓名

  4. // 定义一个接口
    interface ISearchFunc {// 定义一个调用签名(source: string, subing: string): boolean
    }// 定义一个函数
    const search: ISearchFunc = function (source: string, subing: string): boolean {return source.search(subing) > -1
    }console.log(search('床前明月光', '明'))  // true

类类型

  1. 对于传统的 JavaScript 程序我们会使用函数基于原型的继承来创建可重用的组件,但对于熟悉使用面向对象方式的程序员使用这些语法就有些棘手,因为他们用的是基于类的继承并且对象是由类构建出来的。 从 ECMAScript 2015,也就是 ES6 开始, JavaScript 程序员将能够使用基于类的面向对象的方式。 使用 TypeScript,我们允许开发者现在就使用这些特性,并且编译后的 JavaScript 可以在所有主流浏览器和平台上运行,而不需要等到下个 JavaScript 版本

  2. 接口和接口之间叫继承(使用关键字:extends),接口和类之间叫实现(使用关键字:implements)

  3. 基本使用

    // 定义一个接口
    interface IFly {// 该方法没有任何的实现(方法总什么都没有)fly()
    }// 定义一个类
    class Person implements IFly {fly() {console.log('剑光如我,斩尽芜杂');}
    }// 实例化对象
    let person = new Person()
    person.fly()
  4. 一个类可以实现多个接口

    // 定义一个接口
    interface ISwim {swim()
    }
    // 定义一个类 ,这个类的类型是IFly和ISwim(当前这个类可以实现多个接口,一个类同时也可以被多个类约束)
    class Person2 implements IFly, ISwim {fly() {console.log('剑光如我,斩尽芜杂');}swim() {console.log('哎呦呦');}
    }// 实例化
    let person2 = new Person2()
    person2.fly()  // 剑光如我,斩尽芜杂
    person2.swim()  // 哎呦呦// 总结:类可以通过接口的方式,来定义当前的这个类的类型
    //      类可以实现一个接口,类也可以实现多个接口,要注意接口中的内容都要真实的实现
  5. 接口继承接口

    // 定义一个接口,继承其他接口
    interface gather extends IFly, ISwim { }// 定义一个类,直接实现 gather 接口
    class Person3 implements gather {fly() {console.log('剑光如我,斩尽芜杂');}swim() {console.log('哎呦呦');}
    }// 实例化
    let person3 = new Person3()
    console.log(person3);
    person3.fly()  // 剑光如我,斩尽芜杂
    person3.swim()  // 哎呦呦

  1. 类可以理解为一个模板,通过模板可以实例化对象

  2. 面向对象的编程思想

  3. // ts中类的定义和使用
    // 定义属性
    class Person {name: stringage: numbergender: string// 定义构造函数:为了将来实例化的时候,可以直接将属性的值进行初始化constructor(name: string, age: number, gender: string) {this.name = namethis.age = agethis.gender = gender}aaa() {console.log(`大家好,我叫${this.name},今年${this.age}岁了,是个${this.gender}孩子`);}
    }let person = new Person('张三', 18, '男')
    person.aaa() // 大家好,我叫张三,今年18岁了,是个男孩子

继承

  1. 继承:类与类之间的关系

  2. 叫法:A类继承B类,那么A类叫做子类,B类叫做基类

    子类 —> 派生类

    基类 —> 超类(父类)

  3. 一旦发生了继承关系,那就出现了父子类的关系

  4. class Person {// 定义属性name: stringage: numbergender: string
    // 定义构造函数constructor(name: string = '占山', age: number = 18, gender: string = '男') {this.name = namethis.age = agethis.gender = gender}
    // 定义实现方法aaa(txt:string){console.log(`你好${txt},我是${this.name}`);}}class Student extends Person{constructor(name:string,age:number,gender:string){super(name,age,gender)}bbb(){super.aaa('莉丝')console.log('张三/李四');}}let person = new Person()let student = new Student(undefined,undefined,undefined)person.aaa('王武') // 你好王武,我是占山student.bbb() //你好莉丝,我是占山   |  张三/李四

多态

  1. 父组件的引用指向子组件的对象,不同类型的对象针对相同的方法产生不同的行为

    // 基类(超类/父类)
    class Animal {// 定义一个属性name: string// 定义一个构造函数constructor(name: string) {// 更新属性值this.name = name}// 实例方法run(distance: number = 0): void {console.log(`跑了${distance}米,${this.name}`);}
    }// 子类1
    class Dog extends Animal {// 构造函数constructor(name: string) {super(name)}// 实例方法,重写父类中的实例方法run(distance: number = 10): void {console.log(`跑了${distance}米,${this.name}`);}
    }// 子类2
    class Pig extends Animal {// 构造函数constructor(name: string) {super(name)}// 实例方法,重写父类中的实例方法run(distance: number = 20): void {console.log(`跑了${distance}米,${this.name}`);}
    }const animal: Animal = new Animal('动物')
    const dog: Dog = new Dog('小狗')
    const pig: Pig = new Pig('小猪')
    animal.run() // 跑了0米,动物
    dog.run() // 跑了10米,小狗
    pig.run() // 跑了20米,小猪const dog1: Animal = new Dog('大狗')
    const pig1: Animal = new Pig('大狗')
    dog1.run() // 跑了10米,大狗
    pig1.run() // 跑了20米,大狗

公共、私有与受保护的修饰符

  1. 修饰符(类中成员的修饰符):主要是描述类中成员(属性、构造函数、方法)的可访问方法
  2. 类中的成员都有自己的默认的访问方法:public
  3. public修饰符:默认修饰符,代表的是公共的,任何位置都可以访问类中的成员
  4. private修饰符:类中的成员如果使用private修饰符,那么外部是无法访问这个成员数据的,当然子类也是无法访问的
  5. protected修饰符:类中的成员如果使用protected修饰符,那么外部是无法访问这个成员数据的,子类也是可以访问的
  // 定义一个类class Person {// public name: string// private name: stringprotected name: string// 构造函数constructor(name: string) {this.name = name}// 方法public user(): void {console.log('你好', this.name);}}// 定义一个子类class Student extends Person {constructor(name: string) {super(name)}user2(): void {// console.log('我很好', this.name); // 使用private修饰符时报错:属性“name”为私有属性,只能在类“Person”中访问console.log('我很好', this.name);}}const person = new Person('黎明')const student = new Student('红豆')person.user()// console.log(person.name);  // 使用private修饰符时无法访问student.user2()

readonly修饰符

  1. readonly(只读)修饰符:首先是一个关键字,对类中的成员进行修饰,修饰后,该属性成员,不可以再外部被随意修改

  2. 构造函数中,可以对只读的属性成员进行修改

  3. 如果构造函数中没有任何参数,类中的属性成员此时已经使用readonly进行修饰了,那么外部也是不能对属性进行修改的

  4. 构造函数中的参数使用readonly进行修饰,一旦修饰了,那么该类中就有了这个可读的成员属性了,外部可以访问,但是不可以修改

  5. 构造函数中的参数使用readonly、public、private、protected进行修饰,无论是哪个修饰符,该类中都会自动添加这么一个属性成员

    (() => {class Person {readonly name: string = '马六'constructor(name?: string) {this.name = namethis.name = '王五'}sayHi() {console.log(`我叫:${this.name}`);}}// 实例化对象const person = new Person('张三')person.sayHi()  // 我叫:张三console.log(person);  // {name: '张三'}console.log(person.name); // 张三// person.name = '李四'  // 报错:无法分配到 "name" ,因为它是只读属性。class Person2 {// 构造函数的name参数,一旦使用readonly进行修饰后,那么该name参数也可以叫参数属性// 构造函数的name参数,一旦使用readonly进行修饰后,那么Person中就有了一个叫做name的属性成员// 构造函数的neme参数,一旦使用readonly进行修饰后,外部意识无法修改类中的name属性成员的// 也可以使用public、private、protectedconstructor(readonly name?: string) {this.name = name}sayHi2() {console.log(`我喜欢${this.name}`);}}let person2 = new Person2('羽毛球')person2.sayHi2()  // 我喜欢羽毛球console.log(person2.name); // 羽毛球// person2.name = '篮球'  // 报错:无法分配到 "name" ,因为它是只读属性。})()

存取器

  1. 让我们可以有效的控制对象中成员的访问,通过getter和setter来进行操作

    (() => {class Person {firstName: stringlastName: string// 外部可以访问,也可以修改// constructor(firstName: string, lastName: string) {//   this.firstName = firstName//   this.lastName = lastName// }get name(): string {return this.firstName + this.lastName}set name(txt) {let names = txt.split('_')this.firstName = names[0]this.lastName = names[1]}}let person = new Person()console.log(person); // {}person.name = '东方_不败'console.log(person); // {firstName: '东方', lastName: '不败'}console.log(person.name); // 东方不败})()

静态成员

  1. 静态成员:在类中通过static修饰的属性或方法,就是静态的属性/方法,也叫做静态成员

  2. 静态成员在是通的过程中是通过:类名.属性名、类名.方法明

  3. (() => {class Person {name: stringconstructor(name: string) {this.name = name}sayHi(): void {console.log(`你好,${this.name}`);}}const person: Person = new Person(`张三`)console.log(person); // {name: '张三'}person.sayHi() // 你好,张三console.log('================================分割==============================');class Person2 {// static中默认有一个name// 静态属性static name1: string = '李四'// 构造函数不能使用staticconstructor(name?: string) {// this.name = name}// 静态方法sayHi(): void {console.log(`你好,${Person2.name1}`);}}const person2: Person2 = new Person2()console.log(person2); // {}console.log(Person2.name1); // 李四Person2.name1 = '王五'console.log(Person2.name1); // 王五person2.sayHi() // 你好,王五})()

抽象类

  1. 抽象类包含抽象方法(抽象方法一般没有任何具体内容的实现),是可以包含实例方法,抽象类是不能被实例化的,为了将子类进行实例化及实现内部的抽象方法

  2. 抽象类的目的或作用都是为了子类服务的

  3. (() => {abstract class Animal {name: string// 抽象属性abstract age: number// 抽象方法abstract eat()// 报错:方法“eat”不能具有实现,因为它标记为抽象。// abstract eat(){//   console.log(`123`);// }sayHi() {console.log(`你好`);}}class Dog extends Animal {age: number = 18// 重新实现抽象类中的方法,这个方法就是当前Dog类的实例方法了eat() {console.log(`我很好`);}}// 实例化Dog的对象// const anm:Animal = new Animal() // 报错:无法创建抽象类的实例。const dog: Dog = new Dog()dog.sayHi()dog.eat()})()

基本示例

和 JavaScript 一样,TypeScript 函数可以创建有名字的函数和匿名函数。你可以随意选择适合应用程序的方式,不论是定义一系列 API 函数还是只使用一次的函数。

通过下面的例子可以迅速回想起这两种 JavaScript 中的函数:

// 命名函数
function add(x, y) {return x + y
}// 匿名函数
let myAdd = function(x, y) { return x + y;
}

#函数类型

#为函数定义类型

让我们为上面那个函数添加类型:

function add(x: number, y: number): number {return x + y
}let myAdd = function(x: number, y: number): number { return x + y
}

我们可以给每个参数添加类型之后再为函数本身添加返回值类型。TypeScript 能够根据返回语句自动推断出返回值类型。

#书写完整函数类型

现在我们已经为函数指定了类型,下面让我们写出函数的完整类型。

let myAdd2: (x: number, y: number) => number =
function(x: number, y: number): number {return x + y
}

#可选参数和默认参数

TypeScript 里的每个函数参数都是必须的。 这不是指不能传递 nullundefined 作为参数,而是说编译器检查用户是否为每个参数都传入了值。编译器还会假设只有这些参数会被传递进函数。 简短地说,传递给一个函数的参数个数必须与函数期望的参数个数一致。

JavaScript 里,每个参数都是可选的,可传可不传。 没传参的时候,它的值就是 undefined。 在TypeScript 里我们可以在参数名旁使用 ? 实现可选参数的功能。 比如,我们想让 lastName 是可选的:

在 TypeScript 里,我们也可以为参数提供一个默认值当用户没有传递这个参数或传递的值是 undefined 时。 它们叫做有默认初始化值的参数。 让我们修改上例,把firstName 的默认值设置为 "A"

function buildName(firstName: string='A', lastName?: string): string {if (lastName) {return firstName + '-' + lastName} else {return firstName}
}console.log(buildName('C', 'D'))
console.log(buildName('C'))
console.log(buildName())

#剩余参数

必要参数,默认参数和可选参数有个共同点:它们表示某一个参数。 有时,你想同时操作多个参数,或者你并不知道会有多少参数传递进来。 在 JavaScript 里,你可以使用 arguments 来访问所有传入的参数。

在 TypeScript 里,你可以把所有参数收集到一个变量里:
剩余参数会被当做个数不限的可选参数。 可以一个都没有,同样也可以有任意个。 编译器创建参数数组,名字是你在省略号( ...)后面给定的名字,你可以在函数体内使用这个数组。

function info(x: string, ...args: string[]) {console.log(x, args)
}
info('abc', 'c', 'b', 'a')

#函数重载

函数重载: 函数名相同, 而形参不同的多个函数
在JS中, 由于弱类型的特点和形参与实参可以不匹配, 是没有函数重载这一说的 但在TS中, 与其它面向对象的语言(如Java)就存在此语法

/*
函数重载: 函数名相同, 而形参不同的多个函数
需求: 我们有一个add函数,它可以接收2个string类型的参数进行拼接,也可以接收2个number类型的参数进行相加
*/// 重载函数声明
function add (x: string, y: string): string
function add (x: number, y: number): number// 定义函数实现
function add(x: string | number, y: string | number): string | number {// 在实现上我们要注意严格判断两个参数的类型是否相等,而不能简单的写一个 x + yif (typeof x === 'string' && typeof y === 'string') {return x + y} else if (typeof x === 'number' && typeof y === 'number') {return x + y}
}console.log(add(1, 2))
console.log(add('a', 'b'))
// console.log(add(1, 'a')) // error

5. 泛型

指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定具体类型的一种特性。

#引入

下面创建一个函数, 实现功能: 根据指定的数量 count 和数据 value , 创建一个包含 countvalue 的数组 不用泛型的话,这个函数可能是下面这样:

function createArray(value: any, count: number): any[] {const arr: any[] = []for (let index = 0; index < count; index++) {arr.push(value)}return arr
}const arr1 = createArray(11, 3)
const arr2 = createArray('aa', 3)
console.log(arr1[0].toFixed(), arr2[0].split(''))

#使用函数泛型

function createArray2 <T> (value: T, count: number) {const arr: Array<T> = []for (let index = 0; index < count; index++) {arr.push(value)}return arr
}
const arr3 = createArray2<number>(11, 3)
console.log(arr3[0].toFixed())
// console.log(arr3[0].split('')) // error
const arr4 = createArray2<string>('aa', 3)
console.log(arr4[0].split(''))
// console.log(arr4[0].toFixed()) // error

#多个泛型参数的函数

一个函数可以定义多个泛型参数

function swap <K, V> (a: K, b: V): [K, V] {return [a, b]
}
const result = swap<string, number>('abc', 123)
console.log(result[0].length, result[1].toFixed())

#泛型接口

在定义接口时, 为接口中的属性或方法定义泛型类型
在使用接口时, 再指定具体的泛型类型

interface IbaseCRUD <T> {data: T[]add: (t: T) => voidgetById: (id: number) => T
}class User {id?: number; //id主键自增name: string; //姓名age: number; //年龄constructor (name, age) {this.name = namethis.age = age}
}class UserCRUD implements IbaseCRUD <User> {data: User[] = []add(user: User): void {user = {...user, id: Date.now()}this.data.push(user)console.log('保存user', user.id)}getById(id: number): User {return this.data.find(item => item.id===id)}
}const userCRUD = new UserCRUD()
userCRUD.add(new User('tom', 12))
userCRUD.add(new User('tom2', 13))
console.log(userCRUD.data)

#泛型类

在定义类时, 为类中的属性或方法定义泛型类型 在创建类的实例时, 再指定特定的泛型类型

class GenericNumber<T> {zeroValue: Tadd: (x: T, y: T) => T
}let myGenericNumber = new GenericNumber<number>()
myGenericNumber.zeroValue = 0
myGenericNumber.add = function(x, y) {return x + y
}let myGenericString = new GenericNumber<string>()
myGenericString.zeroValue = 'abc'
myGenericString.add = function(x, y) { return x + y
}console.log(myGenericString.add(myGenericString.zeroValue, 'test'))
console.log(myGenericNumber.add(myGenericNumber.zeroValue, 12))

#泛型约束

如果我们直接对一个泛型参数取 length 属性, 会报错, 因为这个泛型根本就不知道它有这个属性

// 没有泛型约束
function fn <T>(x: T): void {// console.log(x.length)  // error
}

我们可以使用泛型约束来实现

interface Lengthwise {length: number;
}// 指定泛型约束
function fn2 <T extends Lengthwise>(x: T): void {console.log(x.length)
}

我们需要传入符合约束类型的值,必须包含必须 length 属性:

fn2('abc')
// fn2(123) // error  number没有length属性

6. 其它

#声明文件

当使用第三方库时,我们需要引用它的声明文件,才能获得对应的代码补全、接口提示等功能

什么是声明语句

假如我们想使用第三方库 jQuery,一种常见的方式是在 html 中通过 <script> 标签引入 jQuery,然后就可以使用全局变量 $jQuery 了。

但是在 ts 中,编译器并不知道 $ 或 jQuery 是什么东西

/*
当使用第三方库时,我们需要引用它的声明文件,才能获得对应的代码补全、接口提示等功能。
声明语句: 如果需要ts对新的语法进行检查, 需要要加载了对应的类型说明代码declare var jQuery: (selector: string) => any;
声明文件: 把声明语句放到一个单独的文件(jQuery.d.ts)中, ts会自动解析到项目中所有声明文件
下载声明文件: npm install @types/jquery --save-dev
*/jQuery('#foo');
// ERROR: Cannot find name 'jQuery'.

这时,我们需要使用 declare var 来定义它的类型

declare var jQuery: (selector: string) => any;jQuery('#foo');

declare var 并没有真的定义一个变量,只是定义了全局变量 jQuery 的类型,仅仅会用于编译时的检查,在编译结果中会被删除。它编译结果是:

jQuery('#foo');

一般声明文件都会单独写成一个 xxx.d.ts 文件

创建 01_jQuery.d.ts, 将声明语句定义其中, TS编译器会扫描并加载项目中所有的TS声明文件

declare var jQuery: (selector: string) => any;

很多的第三方库都定义了对应的声明文件库, 库文件名一般为 @types/xxx, 可以在 https://www.npmjs.com/package/package 进行搜索

有的第三库在下载时就会自动下载对应的声明文件库(比如: webpack),有的可能需要单独下载(比如jQuery/react)

#内置对象

JavaScript 中有很多内置对象,它们可以直接在 TypeScript 中当做定义好了的类型。

内置对象是指根据标准在全局作用域(Global)上存在的对象。这里的标准是指 ECMAScript 和其他环境(比如 DOM)的标准。

  1. ECMAScript 的内置对象

Boolean
Number
String
Date
RegExp
Error

/* 1. ECMAScript 的内置对象 */
let b: Boolean = new Boolean(1)
let n: Number = new Number(true)
let s: String = new String('abc')
let d: Date = new Date()
let r: RegExp = /^1/
let e: Error = new Error('error message')
b = true
// let bb: boolean = new Boolean(2)  // error
  1. BOM 和 DOM 的内置对象

Window
Document
HTMLElement
DocumentFragment
Event
NodeList

const div: HTMLElement = document.getElementById('test')
const divs: NodeList = document.querySelectorAll('div')
document.addEventListener('click', (event: MouseEvent) => {console.dir(event.target)
})
const fragment: DocumentFragment = document.createDocumentFragment()

TypeScript学习相关推荐

  1. RUST直接升钢指令_[译]参照TypeScript学习Rust-part-1

    [译]参照TypeScript学习Rust-1 · 前端在线​regx.vip 对于前端,笔者比较认可Rust作为前端开发技术栈投资的,本文系列翻译旨在分享.学习Rust这门语言. Rust常常被认为 ...

  2. TypeScript学习笔记3:运算符

    TS 和 JS 相对比的优势 TypeScript的安装步骤.运行问题及代码的简单运行 TypeScript学习笔记1:变量赋值及书写方式 TypeScript学习笔记2:数据类型 文章目录 运算符 ...

  3. TypeScript学习笔记2:数据类型

    TS 和 JS 相对比的优势 TypeScript的安装步骤.运行问题及代码的简单运行 TypeScript学习笔记1:变量赋值及书写方式 TypeScript学习笔记2:数据类型 文章目录 数据类型 ...

  4. TypeScript学习笔记1:变量赋值及书写方式

    TS 和 JS 相对比的优势 TypeScript的安装步骤.运行问题及代码的简单运行 TypeScript学习笔记1:变量赋值及书写方式 TypeScript学习笔记2:数据类型 文章目录 变量赋值 ...

  5. Typescript 学习笔记七:泛型

    中文网:https://www.tslang.cn/ 官网:http://www.typescriptlang.org/ 目录: Typescript 学习笔记一:介绍.安装.编译 Typescrip ...

  6. typescript 接口 java_[Java教程]【TypeScript】TypeScript 学习 2——接口

    [Java教程][TypeScript]TypeScript 学习 2--接口 0 2015-06-19 12:00:28 在 TypeScript 中,接口是用作约束作用的,在编译成 JavaScr ...

  7. TypeScript 学习一 参数,函数,析构表达式

    1,TypeScript是由微软开发的,不过新出的Angular2框架就是谷歌公司由TypeScript语言编写的,所以现在TypeScript是有微软和谷歌一起支持的: 2,TypeScript在j ...

  8. Typescript 学习笔记一:介绍、安装、编译

    前言 整理了一下 Typescript 的学习笔记,方便后期遗忘某个知识点的时候,快速回忆. 为了避免凌乱,用 gitbook 结合 marketdown 整理的. github地址是:ts-gitb ...

  9. TypeScript学习总结

    TypeScript学习总结 前言 一.TypeScript是什么? 二.JavaScript 与 TypeScript 的区别 三.TypeScript基础 3.3.TypeScript 基础语法 ...

  10. typescript学习之路(三) —— ts定义类的方法(包含es5以及es6的定义类)

    提起类,不得不说一下,强类型编程语言,如php,java,c++等都有类的概念.而js作为一门弱类型语言,是没有类这个概念的,虽然也能模拟类的实现,但总归不是类.so,ts也只是模拟类而已,使得更贴切 ...

最新文章

  1. 【Android 逆向】x86 汇编 ( 使用 IDA 解析 x86 架构的动态库文件 | 使用 IDA 打开动态库文件 | IDA 中查找指定的方法 )
  2. java.sql.SQLException: Io 异常: Got minus one from a read call
  3. 使用Java扫描DynamoDB项目
  4. 【原】git命令行查看全部分支与远程分支不同步问题
  5. netty冲突 play sbt_《Netty官方文档》本地传输接口
  6. App 运营的指标具体都有哪些?
  7. Nginx 默认的日志类型
  8. b站怎么删自己的专栏_麦当劳B站直播翻车,品牌B站营销到底应该怎么做?
  9. python-获取当前文件名
  10. PHP 开发者如何做代码审查?
  11. Java POI 删除最后一页空白页
  12. 有刷直流电机和无刷直流电机有什么区别?
  13. 五线谱音名和组别对照表_五线谱最全知识及符号! 太实用了,100%收藏!!!...
  14. 管网模型(julia)
  15. iOS App构建版本
  16. Wechaty创建属于自己的微信机器人(附源码)
  17. JavaScript中递归函数
  18. Python今日编程——判断水仙花数然后求水仙花数
  19. Vue项目启动原理及项目的创建
  20. 民航导航技术发展及北斗应用分析

热门文章

  1. 关于 电子护照 的基本小常识问答
  2. 2022年终总结(脚踏实地,仰望星空)
  3. 32/64位处理器、操作系统、应用程序和库之间有什么关系?
  4. 逻辑回归分类——信用卡诈骗
  5. BZOJ 3505: [Cqoi2014]数三角形|组合数学
  6. 【职场加油站】给职场新人的几条忠告
  7. 江苏大学京江学院计算机,江苏大学京江学院
  8. php转调页面,转调踏莎行上巳道中作
  9. java聊天室登录页面_做好的Java聊天室怎么加登录功能,代码如下
  10. 安装fastdfs http访问文件