初识TypeScript
学习ts之前, 应当对js有一定的了解
简介
TS包含JS, 是JS的超集, 需要编译才能被浏览器识别. 全局安装TS
$npm install -g typescript
没有修改
npm为国内源的可以执行以下代码
$npm config set registry http://registry.npm.taobao.org $npm get registry http://registry.npm.taobao.org/
查看TS版本:
$>tsc -V Version 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 $ls index.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自动编译
- 执行命令, 生成
tsconfig.json
文件$tsc --init
- 修改配置
主要修改:
"outDir": "./js", /* 把编译后的文件放到js目录下 */ "strict": false, /* 不使用严格模式 */
- 启动监听任务
$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: string lastName: string fullName: string // 定义一个构造函数 constructor(firstNme: string, lastName: string) { // 更新属性数据 this.firstName = firstNme this.lastName = lastName this.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
打包htmlclean-webpack-plugin
清除打包过程中的目录ts-loader
ts编译工具cross-env
跨平台命令支持
创建文件
文件目录结构:
├─build ├─public └─src
- 入口JS:
src/main.ts
document.write('Hello Webpack TS!')
- index页面:
public/index.html
<!DOCTYPE html> <html lang="en"> <head> <title>webpack & TS</title> </head> <body> </body> </html>
- 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 = true console.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 = null let b: undefined = undefined let name: string = null name = "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] // OK t1 = [10, 'hello'] // Error })()
枚举
enum类型是对
js标准数据类型的一个补充
(() => { enum Color { Red, Green, Blue } // 枚举数值默认从0开始依次递增 // 根据特定的名称得到对应的枚举数值 let myColor: Color = Color.Green // 0 console.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: any v = 1 v = "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') // error console.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 age console.log(`name type: ${typeof name}, age type ${typeof age}`) age = 18 })()
接口
接口是对象的状态(属性)和行为(方法)的抽象(描述), 即我们可以使用接口对 对象类型进行类型检查
简单使用
主要是给对象数据做类型注解用的
(() => { interface IPerson { firstName: string lastName: string } function fullName(person: IPerson) { // 在ide中可以直接.出来 return `${person.firstName}${person.lastName}` } let p = { firstName: "东方", lastName: "不败" } console.log(fullName(p)) } )()
可选属性
假如某些属性不是必须的, 默认情况下都是必需的
加个
?即可
(() => { interface IPerson { name: string age: number gender: boolean description?: string // 该属性可以省略 } const person: IPerson = { name: "lczmx", age: 18, gender: true, } console.table(person) } )()
只读属性
一些对象属性只能在对象刚刚创建的时候修改其值
加个
readonly即可
(() => { interface IPerson { readonly id: number name: string age: number gender: 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: string constructor(name) { this.name = name } } // 继承Animal class 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: string public note: string constructor(name, note) { this.name = name this.note = note } } // 继承Animal class 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: string private note: string constructor(name, note) { this.name = name this.note = note } } // 继承Animal class Dog extends Animal { private say(message: string): void { console.log(`${this.name}: ${message}`) } } const dog = new Dog("小黄", "宠物") // 访问私有属性会报错 // 以下两行都会报错 console.log(dog.note) // error dog.say("汪汪汪~") // error })()
受保护修饰符 protected
protected修饰符与
private相似, 但
protected成员在子类中仍然可以访问
(() => { class Animal { protected name: string constructor(name) { this.name = name } } // 继承Animal class 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: string constructor(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: string lastName: string constructor(firstName: string, lastName: string) { this.firstName = firstName this.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) // 触发 get person.fullName = "李 四" // 触发 set console.log(person.fullName) // 触发 get })()
静态属性
静态属性即是存在于类本身上面而不是类的实例上的属性
(() => { class Person { name1: string = 'A' static name2: string = 'B' } // 在类中访问 console.log(Person.name2) // 使用实例不能访问name2, 可以访问name1 const 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 undefined return 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): string function add(x: number, y: number): number // 定义函数实现 function add(x: string | number, y: string | number): string | number { // 在实现上我们要注意严格判断两个参数的类型是否相等,而不能简单的写一个 x + y if (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) // number f<string>("abc") // string f<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('')) // error const 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) => void getById: (id: number) => T } // 存放用户数据 class UserInfo { id?: number; //id主键自增 name: string; //姓名 age: number; //年龄 constructor(name, age) { this.name = name this.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: T add: (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属性
其他
声明文件
在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()
- 初识Typescript
- (一)初识typeScript
- TypeScript快餐教程 (1) - 初识
- typescript初识
- 初识TypeScript
- TypeScript初识
- TypeScript 基础数据类型简介
- 初识shell脚本--基于xtrabackup的自动化全量备份
- JAVA学习第一课(初识JAVA)
- day1 MyEclipse 及初识SPRING
- 第一章 初识C语言
- ActiveMQ初识及安装
- 初识盒子模型
- 初识html之表格
- Linux学习之CentOS(三)--初识linux的文件系统以及用户组等概念
- 初识MySQL
- Android登入界面(初识Android)
- 初识SEO
- 初识nginx
- Spring学习心得-初识Maven