AI智能
改变未来

初识TypeScript

学习ts之前, 应当对js有一定的了解

简介

TS包含JS, 是JS的超集, 需要编译才能被浏览器识别.全局安装TS

$npm install -g typescript

没有修改

npm

为国内源的可以执行以下代码

$npm config set registry http://registry.npm.taobao.org$npm get registryhttp://registry.npm.taobao.org/

查看TS版本:

$>tsc -VVersion 4.5.3

简单使用

先创建一个

ts

文件, 暂时使用

ts

的语法写一段代码

/* ts01.ts 文件 */(() => {function sayHi(str: string) {return "你好" + str}let text = "lczmx";console.log(sayHi((text)));})()

然后使用

html

文件引用:

<!DOCTYPE html><html lang="en"><head><title>Ts01</title></head><body><!-- html文件与ts文件同级 --><script src="./ts01.ts"></script></body></html>

使用浏览器打开, 发现报错, 原因是浏览器无法识别

ts

的语法:最后编译

ts

js

, 再次引用:

$tsc ts01.ts$lsindex.html  ts01.js  ts01.ts

以上命令会在源目录下生成相同文件名的js文件

index.html

:

<!DOCTYPE html><html lang="en"><head><title>Ts01</title></head><body><!-- html文件与js文件同级 --><script src="./ts01.js"></script></body></html>

成功执行:

感兴趣的可以看看编译后的

js

长什么样

TS自动编译

  1. 执行命令, 生成
    tsconfig.json

    文件

    $tsc --init
  2. 修改配置主要修改:
    "outDir": "./js",   /* 把编译后的文件放到js目录下 */"strict": false,   /* 不使用严格模式 */
  3. 启动监听任务
    $tsc -p tsconfig.json --watch[下午11:20:35] Starting compilation in watch mode...[下午11:20:37] Found 0 errors. Watching for file changes.

以上, 当我们修改

ts

文件时,

tsc

就会自动在

js

目录下, 编译成对应的

js

文件

类型注解

即像上面的代码那样使用

:

为参数作类型的约束, 类型不对时, ts会报错, 但仍然会生成对应的js文件.
例子:

(() => {function showMsg(str: string) {return "show: " + str}let message = "hello world";showMsg(message);let m2 = [1, 2, 3];showMsg(m2) // 报错, 但还会生成js文件})()

js中怎样写, ts就怎样写

(() => {// 定义接口interface IPerson {firstName: string  // 姓lastName: string  // 名}// 定义一个类class Person {// 定义公共字段(属性)firstName: stringlastName: stringfullName: string// 定义一个构造函数constructor(firstNme: string, lastName: string) {// 更新属性数据this.firstName = firstNmethis.lastName = lastNamethis.fullName = this.firstName + this.lastName}}// 定义一个函数function showFullName(person: IPerson) {return person.firstName + person.lastName}// 实例化对象const person = new Person("邢", "道荣")console.log(showFullName(person))})()

TypeScript

里的类只是一个语法糖,本质上还是

JavaScript

函数的实现。

使用webpack打包TS

初始化

生成

package.json

tsconfig.json

$npm init -y$tsc --init

下载依赖

$npm install -D typescript$npm install -D webpack webpack-cli webpack-dev-server$npm install -D html-webpack-plugin clean-webpack-plugin$npm install -D ts-loader$npm install -D cross-env

上面这些包的作用:

  • typescript

    提供TS支持

  • webpack

    打包工具

  • webpack-cli

    为webpack提供开发指令

  • webpack-dev-server

    打包时提供的服务器 (可用于开发)

  • html-webpack-plugin

    打包html

  • clean-webpack-plugin

    清除打包过程中的目录

  • ts-loader

    ts编译工具

  • cross-env

    跨平台命令支持

创建文件

文件目录结构:

├─build├─public└─src
  1. 入口JS:
    src/main.ts
document.write(\'Hello Webpack TS!\')
  1. index页面:
    public/index.html
<!DOCTYPE html><html lang="en"><head><title>webpack & TS</title></head><body></body></html>
  1. webpack配置:
    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\'),filename: \'[name].[contenthash:8].js\'},module: {rules: [{test: /\\.tsx?$/,use: \'ts-loader\',include: [resolve(\'src\')]}]},plugins: [new CleanWebpackPlugin({}),new HtmlWebpackPlugin({template: \'./public/index.html\'})],resolve: {extensions: [\'.ts\', \'.tsx\', \'.js\']},devtool: isProd ? \'cheap-module-source-map\' : \'cheap-module-eval-source-map\',devServer: {host: \'localhost\', // 主机名stats: \'errors-only\', // 打包日志输出输出错误信息port: 8081,open: true},}

配置打包命令

package.json

中, 修改

scripts

的内容如下:

"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"

运行与打包

$npm run dev$npm run build

基础类型

布尔值

true

false

(() => {let status: boolean = false;status = trueconsole.log(status)})()

数字

js

一样,

ts

的数字都是浮点数, 这些浮点数的类型都是

number

:

(() => {let a1: number = 10 // 十进制let a2: number = 0b1010  // 二进制let a3: number = 0o12 // 八进制let a4: number = 0xa // 十六进制console.log(a1, a2, a3, a4)// 10 10 10 10})()

字符串

同样与js一样

(() => {let firstName: string = "诸葛"let lastName: string = "亮"console.log(`${firstName}${lastName}`)})()

undefined和null

ts

中,

undefined

null

两者各自有自己的类型分别叫做

undefined

null

它们的本身的类型用处不是很大:

(() => {let a: null = nulllet b: undefined = undefinedlet name: string = nullname = "lczmx"console.log(`username: ${name}`)})()

默认情况下

null

undefined

是所有类型的子类型, 就是说你可以把

null

undefined

赋值给

number

类型的变量

数组

两种方式:

元素类型+[]
Array<元素类型>

(() => {let list1: number[] = [1, 2, 3, 4, 5]let list2: Array<number> = [6, 7, 8, 9]console.table(list1)console.table(list2)})()

元组

元组表示一个已知元素数量和类型的数组, 且各元素的类型不必相同

(() => {let t1: [string, number]t1 = [\'hello\', 10] // OKt1 = [10, \'hello\'] // Error})()

枚举

enum

类型是对

js

标准数据类型的一个补充

(() => {enum Color {Red,Green,Blue}// 枚举数值默认从0开始依次递增// 根据特定的名称得到对应的枚举数值let myColor: Color = Color.Green  // 0console.log(myColor, Color.Red, Color.Blue)// 1 0 2// 指定开始enum Color1 {Red = 1, Green, Blue}let colorName: string = Color1[2]console.log(colorName)  // \'Green\'})()

any

动态类型的数据 (接收用户输入和第三方代码库), 使用

any

类型来标记

(() => {let v: anyv = 1v = "maybe a string"v = false})()

void

表示没有任何类型, 当一个函数没有返回值时, 你通常会见到其返回值类型是

void

(() => {function f(): void {console.log("running test function")}f()})()

声明一个

void

类型的变量没有什么大用, 因为你只能为它赋予

undefined

null

let unusable: void = undefined

object

object

表示非原始类型,也就是除

number
string
boolean

之外的类型

(() => {function fn2(obj: object): object {console.log(\'fn2()\', obj)return {}// return undefined// return null}console.log(fn2(new String(\'abc\')))// console.log(fn2(\'abc\') // errorconsole.log(fn2(String))})()

联合类型

表示取值可以为多种类型中的一种

(() => {function f(x: number | string) {console.log(typeof x)}f(123)f("abc")})()

类型断言

类型断言好比其它语言里的类型转换, 但是不进行特殊的数据检查和解构
两种方式:

<类型>值
值 as 类型

(() => {function getLength(x: number | string) {if ((<string>x).length) {return (x as string).length} else {return x.toString().length}}console.log(getLength(\'abcd\'), getLength(1234))})()

类型推断

ts

会在没有明确的指定类型的时候推测出一个类型

  • 赋值时, 推断为对应的类型
  • 没有赋值, 推断为
    any

    类型

(() => {let name = "lczmx"let ageconsole.log(`name type: ${typeof name}, age type ${typeof age}`)age = 18})()

接口

接口是对象的状态(属性)和行为(方法)的抽象(描述), 即我们可以使用接口对 对象类型进行类型检查

简单使用

主要是给对象数据做类型注解用的

(() => {interface IPerson {firstName: stringlastName: string}function fullName(person: IPerson) {// 在ide中可以直接.出来return `${person.firstName}${person.lastName}`}let p = {firstName: "东方",lastName: "不败"}console.log(fullName(p))})()

可选属性

假如某些属性不是必须的, 默认情况下都是必需的
加个

?

即可

(() => {interface IPerson {name: stringage: numbergender: booleandescription?: string // 该属性可以省略}const person: IPerson = {name: "lczmx",age: 18,gender: true,}console.table(person)})()

只读属性

一些对象属性只能在对象刚刚创建的时候修改其值
加个

readonly

即可

(() => {interface IPerson {readonly id: numbername: stringage: numbergender: boolean}const person: IPerson = {id: 1,name: "lczmx",age: 18,gender: true,}console.table(person)// person.id = 2 //error})()

变量的话用

const

, 属性的话用

readonly

函数类型

我们也可以将函数作为使用接口表示函数
只需要 参数类型 和 返回类型

(() => {interface SearchFunc {(source: string, subString: string): boolean}const mySearch: SearchFunc = function (source: string, sub: string): boolean {return source.search(sub) > -1}console.log(mySearch(\'lczmx\', \'mx\'))})()

类类型

TypeScript 也能够用它来明确的强制一个类去符合某种契约。

  • 一个类可以实现多个接口
  • 一个接口可以继承多个接口

一般使用

(() => {// 定义接口interface Alarm {alert(): any;}// 定义类class Car implements Alarm {alert() {console.log(\'Car alert\');}}})()

多个接口

(() => {// 定义接口interface Alarm {alert(): any;}interface Light {lightOn(): void;lightOff(): void;}// 定义类class Car2 implements Alarm, Light {alert() {console.log(\'Car alert\');}lightOn() {console.log(\'Car light on\');}lightOff() {console.log(\'Car light off\');}}})()

接口继承

(() => {// 定义接口interface Alarm {alert(): any;}interface Light {lightOn(): void;lightOff(): void;}// 继承两个接口interface LightAlarm extends Alarm, Light {test(): void;}// 定义类class Car implements LightAlarm {alert() {console.log(\'Car alert\');}lightOn() {console.log(\'Car light on\');}lightOff() {console.log(\'Car light off\');}test() {console.log("test func")}}})()

从ES6开始, 我们可以使用基于类的面向对象的方式, 在

ts

中, 我们可以使用一些特性,并且编译后的

JavaScript

可以在所有主流浏览器和平台上运行,而不需要等到下个

JavaScript

版本

一般使用

一般来说有三部分组成:

声明属性
定义构造方法
定义一般方法

(() => {class Greeter {// 声明属性message: string// 定义构造方法constructor(message: string) {// 初始化属性this.message = message}// 定义一般方法greet(): string {return `hello ${this.message}`}}// 创建实例const greeter = new Greeter("lczmx")// 调用方法console.log(greeter.greet())// hello lczmx})()

继承

这是面向对象的特性之一, 使用

extends

指定要继承的类

Dog

继承

Animal

(() => {class Animal {name: stringconstructor(name) {this.name = name}}// 继承Animalclass Dog extends Animal {say(message: string): void {console.log(`${this.name}: ${message}`)}}const dog = new Dog("小黄")// 调用say方法dog.say("汪汪汪~")})()

和其他的面向对象的语言一样, 可以使用 "多态", 重写父类的方法即可

修饰符

类似封装

公共修饰符

public

使用

public

指定, 指的是可以让外部访问

默认都为

public

举个例子:

(() => {class Animal {name: stringpublic note: stringconstructor(name, note) {this.name = namethis.note = note}}// 继承Animalclass Dog extends Animal {public say(message: string): void {console.log(`${this.name}: ${message}`)}}const dog = new Dog("小黄", "宠物")// 访问public属性console.log(dog.note)dog.say("汪汪汪~")})()

私有修饰符

private

public

相对于, 指的是不能被外部访问, 包括子类也不能被访问

(() => {class Animal {name: stringprivate note: stringconstructor(name, note) {this.name = namethis.note = note}}// 继承Animalclass Dog extends Animal {private say(message: string): void {console.log(`${this.name}: ${message}`)}}const dog = new Dog("小黄", "宠物")// 访问私有属性会报错// 以下两行都会报错console.log(dog.note)   // errordog.say("汪汪汪~")     // error})()

受保护修饰符

protected

protected

修饰符与

private

相似, 但

protected

成员在子类中仍然可以访问

(() => {class Animal {protected name: stringconstructor(name) {this.name = name}}// 继承Animalclass Dog extends Animal {say(message: string): void {// 可以访问 name属性console.log(`${this.name}: ${message}`)}}const dog = new Dog("小黄")dog.say("汪汪汪~")dog.name // error 不能访问})()

只读修饰符

readonly

你可以使用

readonly

关键字将属性设置为只读的
只读属性必须在声明时或构造函数里被初始化

(() => {class Dog {readonly name: stringconstructor(name) {this.name = name}say(message: string): void {// error// 修改只读属性this.name = "老黄"console.log(`${this.name}: ${message}`)}}const dog = new Dog("小黄")dog.say("汪汪汪~")})()

在构造时指定修饰符

我们可以在构造函数中指定对于的修饰符, 而不需要特意去声明属性

(() => {class Dog {constructor(protected name: string) {this.name = name}say(message: string): void {console.log(`${this.name}: ${message}`)}}const dog = new Dog("小黄")dog.say("汪汪汪~")})()

读取器

TS

中, 我们可以通过

getters/setters

来截取对对象成员的访问, 从而控制对对象的成员的访问

写起来和方法一样

(() => {class Person {firstName: stringlastName: stringconstructor(firstName: string, lastName: string) {this.firstName = firstNamethis.lastName = lastName}// 定义存取器// 获取时访问这个get fullName(): string {return `${this.firstName} ${this.lastName}`}// 设置时访问这个set fullName(value) {const names = value.split(\' \')this.firstName = names[0]this.lastName = names[1]console.log("change firstName lastName")}}// 使用const person = new Person("张", "三")console.log(person.fullName)  // 触发 getperson.fullName = "李 四"     // 触发 setconsole.log(person.fullName)  // 触发 get})()

静态属性

静态属性即是存在于类本身上面而不是类的实例上的属性

(() => {class Person {name1: string = \'A\'static name2: string = \'B\'}// 在类中访问console.log(Person.name2)// 使用实例不能访问name2, 可以访问name1const person = new Person()console.log(person.name1)})()

抽象类

抽象类做为其它派生类的基类使用。 它们不能被实例化
使用

abstract

定义抽象类和在抽象类内部定义抽象方法

(() => {abstract class Animal {abstract cry()run() {console.log(\'execute run()\')}}class Dog extends Animal {cry() {console.log(\'execute Dog cry()\')}}const dog = new Dog()dog.cry()dog.run()})()

函数

ts的函数为js的函数添加了额外的功能, 更加有利于函数的使用

一般使用

大体和

JavaScript

中那样使用

(() => {// 有名称的函数function add(x, y) {return x + y}// 匿名函数let myAdd = function (x, y) {return x + y}console.log(add(1, 2))console.log(myAdd(3, 4))})()

使用类型注解

可以注解参数和返回值

(() => {function add(x: number, y: number): number {return x + y}let myAdd = function (x: number, y: number): number {return x + y}console.log(add(1, 2))console.log(myAdd(3, 4))let myAdd2: (x: number, y: number) => number =function (x: number, y: number): number {return x + y}})()

可选参数和默认参数

在ts中, 每个函数的参数默认都是必需的, 即不能多传也不能少传
在某些场景中, 我们需要参数是可选的或默认的

(() => {function fullName(firstName: string = \'lcz\', lastName?: string): string {if (lastName) {return firstName + lastName} else {console.log(firstName, lastName)    // lcz undefinedreturn firstName}}console.log(fullName())console.log(fullName("lcz", "mx"))})()

可选参数:

?:

, 不传值时, 值为

undefined

剩余参数

可以将多余的参数作为一个数组, 类似python的

*args

(() => {function info(x: string, ...args: string[]) {console.log(`x: ${x}  args: ${args}`)}info("a", "b", "c", "d", "e", "f")})()

函数重载

函数重载主要是解决两个问题: 参数类型不一样、参数个数不一样, 为此需要定义多个函数

(() => {// 重载函数声明function add(x: string, y: string): stringfunction 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})()

泛型

泛型指的是在定义函数、接口或类的时候, 不预先指定具体的类型, 而在使用的时候再指定具体类型的一种特性如下面这个例子:

(() => {function f<T>(a: T) {console.log(typeof a)}f<number>(1)    // numberf<string>("abc")    // stringf<number[]>([1, 2, 3]) // object})()

使用

<>

指定类型, 在使用的时候也可以用

<>

指定具体的类型

一般使用

(() => {function createArray<T>(value: T, count: number) {const arr: Array<T> = []for (let index = 0; index < count; index++) {arr.push(value)}return arr}const arr1 = createArray<number>(11, 3)console.log(arr1[0].toFixed())// console.log(arr3[0].split(\'\')) // errorconst arr2 = createArray<string>(\'aa\', 3)console.log(arr2[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())  // 3 \'123\'})()

泛型接口

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

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

泛型类

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

(() => {class GenericNumber<T> {zeroValue: Tadd: (x: T, y: T) => T}let myGenericNumber = new GenericNumber<number>()myGenericNumber.zeroValue = 0myGenericNumber.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属性

其他

声明文件

在ts中, 假如我们需要用第三方库, 但在ts中不能用thml中的

<script>

标签, 那么我们应该如何引用呢?很简单, 使用

declare var

定义, 例子:

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

注意: 一般无需我们手动定义, 下载即可, 下载声明文件:

npm install @types/jquery --save-dev

declare var

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

jQuery(\'#foo\');

内置对象

JavaScript 中有很多内置对象,它们可以直接在 TypeScript 中当做定义好了的类型内置对象是指根据标准在全局作用域(Global)上存在的对象
这里的标准是指 ECMAScript 和其他环境(比如 DOM)的标准

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

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()
赞(0) 打赏
未经允许不得转载:爱站程序员基地 » 初识TypeScript