您的位置:首页 > Web前端 > JavaScript

初识TypeScript

2021-12-19 21:12 886 查看

学习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自动编译

  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: 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
    打包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 = 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()
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: