一、安装
全局安装
npm install typescript -g
查看版本
tsc --version
二、运行环境
方式一:webpack配置环境
npm init -y
安装依赖
npm i webpack webpack-cli typescript webpack-dev-server ts-loader html-webpack-plugin
package.json
的配置
"scripts": {
"test": "echo "Error: no test specified" && exit 1",
"build": "webpack",
"serve": "webpack serve"
},
创建一个tsconfig.json
配置文件
tsc --init
webpack.config.js
相关的配置
const path = require('path');
const HtmlWebPackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development',
entry: './src/main.ts',
output: {
path: path.resolve(__dirname, './dist'),
filename: 'bundle.js'
},
resolve: {
extensions: ['.js', '.cjs', '.ts', '.json']
},
module: {
rules: [
{
test: /.ts$/,
loader: 'ts-loader'
}
]
},
plugins: [
new HtmlWebPackPlugin({
template: './index.html'
})
]
};
在根路径上创建一个index.html
最后通过启动项目
npm run serve
方式二:ts-node
安装ts-node
,另外ts-node需要依赖 tslib
和 @types/node
两个包
npm install ts-node tslib @types/node -g
运行ts文件
ts-node math.ts
三、变量的声明
1.变量的定义
var/let/const 标识符: 数据类型 = 赋值
2.number类型的使用
let num1: number = 12345
let num2: number = 0b101
let num3: number = 0o567
let num4: number = 0x45def
console.log(num1, num2, num3, num4)
export {}
3.boolean类型的使用
let flag: boolean = true
flag = 20 > 30
console.log(flag) // false
export {}
4.string类型的使用
let message: string = 'hello world'
// 如果可以推断出类型可以不用写
let name = 'yogln'
let age = 18
export {}
5.array类型的使用
// const arr: Array<string> = [] // 不推荐(react jsx中是有冲突 <div></div>)
const arr: string[] = [] // 推荐
arr.push('abc')
arr.push('bbc')
arr.push(123) // 报错
export{}
6.object类型的使用
const obj = {
name: 'yogln',
age: 18
}
console.log(obj.name)
7.null和undefied类型
let n: null = null
let u: undefined = undefined
8.symbol类型的使用
let s1 = Symbol('title')
let s2 = Symbol('title')
const obj = {
[s1]: '前端',
[s2]: '开发'
}
export {}
四、TypeScript数据类型
1.any类型的使用
// 当进行一些类型断言 as any
// 在不想给某些JavaScript添加具体的数据类型时(原生的JavaScript代码是一样)
let message: any = 'Hello World'
message = 123
message = true
message = {}
2.unknown类型的使用
// unknown类型只能赋值给any和unknown类型
// any类型可以赋值给任意类型
function foo(arg: unknown) {
console.log(typeof arg)
}
foo('123')
foo(123)
3.void类型的使用
function sum(num1: number, num2: number): void {
console.log(num1 + num2)
}
sum(20, 30)
void
可以不写
4.never类型的使用
function handleMessage(message: number | string | boolean) {
switch (typeof message) {
case 'number':
console.log('number类型的处理')
break
case 'string':
console.log('strign类型的处理')
break
case 'boolean':
console.log('boolean类型的处理')
break
default:
const check: never = message
}
}
handleMessage('abc')
handleMessage(123)
// 张三
handleMessage(true)
5.tuple元组类型的使用
// tuple 元组类型 多种元素的组合
const info: [string, number, string] = ['yogln', 18, '前端']
const name = info[0]
console.log(name)
export {}
6.tuple元组类型的应用场景
// hook: useState
// const [counter, setCounter] = {counter: , setCounter:}
function useState(state: any) {
let currentState = state
const changeState = (newState: any) => {
currentState = newState
}
const tuple: [any, (newState: any) => void] = [currentState, changeState]
return tuple
}
const [counter, setCounter] = useState(10)
export {}
当然我们也可以优化一下使用泛型
// hook: useState
// const [counter, setCounter] = {counter: , setCounter:}
function useState<T>(state: T) {
let currentState = state
const changeState = (newState: T) => {
currentState = newState
}
const tuple: [T, (newState: T) => void] = [currentState, changeState]
return tuple
}
const [counter, setCounter] = useState(10)
export {}
五、其他类型的补充
1.函数的参数和返回值类型
// 给参数加上类型注解: num1: number, num2: number
// 给返回值加上类型注释: (): number
// 在开发中,通常情况下可以不写返回值的类型(自动推导)
function sum(num1: number, num2: number) {
return num1 + num2
}
// sum(123, 321)
所以一般不用写返回值的类型
匿名函数可以通过推到得出参数的类型和返回值的类型
const names = ["abc", "cba", "nba"]
// item根据上下文的环境推导出来的, 这个时候可以不添加的类型注解
// 上下文中的函数: 可以不添加类型注解
names.forEach(function(item) {
console.log(item.split(""))
})
2.对象类型
function printPoint(point: { x: number; y: number }) {
console.log(point.x, point.y)
}
printPoint({ x: 2, y: 4 })
3.可选类型 ?
z?: number
function printPoint(point: { x: number; y: number; z?: number }) {
console.log(point.x)
console.log(point.y)
console.log(point.z)
}
printPoint({ x: 123, y: 321 })
printPoint({ x: 123, y: 321, z: 111 })
export {}
4.联合类型 |
function print(id: number | string) {
console.log(id)
}
print(123)
print('abc')
export {}
5.类型别名 type
type PointType = {
x: number
y: number
z?: number
}
function printPoint(point: PointType) {
console.log(point.x, point.y, point.z)
}
printPoint({ x: 2, y: 2 })
6.类型断言 as
// 1. 类型断言 as
// <img id="why"/>
const el = document.getElementById('yogln') as HTMLImageElement
el.src = '图片的url'
// 2. Person 是 Student 父类
class Person {}
class Student extends Person {
studying() {}
}
function sayHello(p: Person) {
;(p as Student).studying()
}
const stu = new Student()
sayHello(stu)
// 3. as any/unknown
let message = 'hello world'
const num: number = message as any as number
7.非空类型断言 !.
打印可选类型的length
function printMsgLength(msg?: string) {
console.log(msg!.length)
}
printMsgLength('hello world')
8.可选链的使用 ?.
type Person = {
name: string
age: number
friend: {
name: string
girlfriend?: {
name: string
}
}
}
const info: Person = {
name: 'yogln',
age: 18,
friend: {
name: 'why'
}
}
console.log(info.friend?.name) // why
console.log(info.friend?.girlfriend?.name) //undefined
export {}
9.!!运算符
const message = 'Hello World'
console.log(!message) //false
console.log(!!message) //true
10.??运算符
let message: string | null = 'Hello World'
const content = message ?? '你好啊, 李银河'
// const content = message ? message: "你好啊, 李银河"
console.log(content) //你好啊, 李银河
11.字面量类型 let num: 123 = 123
const msg: 'hello world' = 'hello world'
let num: 123 = 123 // num 字面量类型的值必须和字面量相同
// num = 234 // 报错
type Alignment = 'left' | 'right' | 'center'
let alignment: Alignment = 'left'
alignment = 'center'
12.字面量类型的推理
type Method = 'GET' | 'POST'
function request(url: string, method: Method) {}
const options = {
url: 'https://www.yogln.com',
method: 'POST'
} as const
request(options.url, options.method)
export {}
13.类型缩小
-
typeof 类型缩小
// 1.typeof的类型缩小 type IdType = string | number function printType(id: IdType) { if (typeof id === 'string') { console.log(id.toUpperCase()) } else { console.log(id) } }
-
平等类型缩小
// 2. 平等类型缩小 type Direction = 'left' | 'right' function pintDirection(direction: Direction) { // 1.if判断 // if (direction === 'left') { // console.log(direction) // } else if () // 2.switch判断 // switch (direction) { // case 'left': // console.log(direction) // break; // case ... // } }
-
instanceof
// 3. instanceof function printTime(time: string | Date) { if (time instanceof Date) { console.log(time.toUTCString()) } else { console.log(time) } } class Student { studying() {} } class Teacher { teaching() {} } function work(p: Student | Teacher) { if (p instanceof Student) { p.studying() } else { p.teaching() } } const stu = new Student() work(stu)
-
in
// 4. in type Fish = { swimming: () => void } type Dog = { running: () => void } function walk(animal: Fish | Dog) { if ('swimming' in animal) { animal.swimming() } else { animal.running() } } const fish: Fish = { swimming() { console.log('swimming') } } walk(fish)
六、TypeScript函数
1.函数的类型
- 定义常量的时候函数的编写的类型
type AddType = (num1: number, num2: number) => number
const add: AddType = (num1: number, num2: number) => {
return num1 + num2
}
- 函数作为函数的参数的时候
// 1. 函数做参数
function bar() {}
type FnType = () => void
function foo(fn: FnType) {
fn()
}
foo(bar)
2.函数类型的案例
function calc(n1: number, n2: number, fn: (n1: number, n2: number) => number) {
return fn(n1, n2)
}
const res1 = calc(20, 30, function (n1, n2) {
return n1 + n2
})
console.log(res1) //50
const res2 = calc(20, 30, function (n1, n2) {
return n1 * n2
})
console.log(res2) //600
3.函数的可选类型
函数的可选类型必须写在必选类型的后面
// y -> undefined | number
function foo(x: number, y?: number) {
}
foo(20, 30)
foo(20)
4.函数参数的默认值
function foo(num1: number, num2: number = 20) {
console.log(num1, num2)
}
foo(20) // 20, 20
5.函数的剩余参数
function total(initial: number, ...nums: number[]): number {
let res = initial
for (const num of nums) {
res += num
}
return res
}
console.log(total(20, 30)) //50
console.log(total(20, 30, 40)) //90
6.this的默认推导
const obj = {
name: 'yogln',
eating() {
console.log(this.name + ' eating')
}
}
obj.eating() //yogln eating
7.this的绑定
type ThisType = {
name: string
}
function eating(this: ThisType, msg: string) {
console.log(this.name, msg)
}
const info = {
name: 'yogln',
eating: eating
}
// 隐式绑定
info.eating('哈哈哈') // yogln 哈哈哈
// 显式绑定
info.eating.call({ name: '显式绑定' }, '显式绑定') // 显式绑定 显式绑定
export {}
注意call
和apply
的区别
apply
把需要传递给fn
的参数放到一个数组(或者类数组)中传递进去,虽然写的是一个数组,但是也相当于给fn
一个个的传递
fn.call(obj, 1, 2);
fn.apply(obj, [1, 2]);
bind
语法和call一模一样,区别在于立即执行还是等待执行
fn.call(obj, 1, 2); // 改变fn中的this,并且把fn立即执行
fn.bind(obj, 1, 2); // 改变fn中的this,fn并不执行
8.函数的重载
function add(num1: number, num2: number): number
function add(num1: string, num2: string): string
function add(num1: any, num2: any): any {
if (typeof num1 === 'string' && typeof num2 === 'string') {
return num1.length + num2.length
}
return num1 + num2
}
console.log(add(123, 321))
console.log(add('yogln', 'ts'))
export {}
七、TypeScript中类的定义
1.类的定义
class Person {
name: string
age: number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
eating() {
console.log(this.name + ' eating')
}
}
const p = new Person('yogln', 18)
console.log(p)
p.eating()
2.类的继承
class Person {
name: string
age: number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
eating() {}
}
class Student extends Person {
status: string
constructor(name: string, age: number, status: string) {
super(name, age)
this.status = status
}
// 重写父类方法
eating() {
console.log(this.name + ' eating')
}
}
const s = new Student('yogln', 18, 'student')
console.log(s)
s.eating()
3.类的多态
class Animal {
moving() {
console.log('animal moving')
}
}
class Dog extends Animal {
moving() {
console.log('dog running')
}
}
class Bird extends Animal {
moving() {
console.log('bird flying')
}
}
function makeMove(animals: Animal[]) {
animals.forEach((animal) => {
animal.moving()
})
}
makeMove([new Dog(), new Bird()])
// dog running
// bird flying
4.成员属性private
私有成员属性,创建的实例和外部没办法直接调用和修改,可以调用里面的方法进行获取和调用
class Person {
private name: string = ''
setName(name: string) {
this.name = name
}
getName() {
return this.name
}
}
const p = new Person()
// console.log(p.name)// 直接报错
console.log(p.getName()) //
p.setName('yogln')
console.log(p.getName()) // yogln
5.成员属性 protected
保护成员属性外部也没法直接调用,但是他的子类继承可以使用
class Person {
protected name: string = 'yogln'
}
class Student extends Person {
getName() {
return this.name
}
}
const stu = new Student()
console.log(stu.getName()) // yogln
6.只读属性readonly
- 只读属性可以在构造器中进行赋值
- 属性本身没法修改,但是如果他是对象类型,对象中的属性值是可以修改的
class Person {
readonly name: string
age?: number
readonly friend?: Person
constructor(name: string, friend?: Person) {
this.name = name
this.friend = friend
}
}
const p1 = new Person('yogln')
// p1.name = 'why' // 直接报错
const p2 = new Person('yogln', new Person('Jhon'))
if (p2.friend) {
p2.friend.age = 20
}
console.log(p2.friend)
7.setter和getter
类中的私有成员是不能进行访问的,这个时候我们可以使用sertter和getter
class Person {
private _name: string
constructor(_name: string) {
this._name = _name
}
set name(newName) {
this._name = newName
}
get name() {
return this._name
}
}
const p = new Person('yogln')
p.name = 'kobe'
console.log(p.name)
8.类的静态成员属性static
可以不用创建实例直接调用类中的属性和方法
class Student {
static stuName: string = 'yogln'
static studying() {
console.log(this.stuName + ' studying')
}
}
console.log(Student.stuName) //yogln
Student.studying() // yogln studying
9.抽象类acstracts
抽象类中的类名和方法都需要abstract
进行修饰
abstract class Shape {
abstract getArea(): number
}
class Rectangle extends Shape {
private x: number
private y: number
constructor(x: number, y: number) {
super()
this.x = x
this.y = y
}
getArea() {
return this.x * this.y
}
}
class Circle extends Shape {
private radius: number
constructor(radius: number) {
super()
this.radius = radius
}
getArea() {
return this.radius * this.radius * 3.14
}
}
const circle = new Circle(2)
console.log(circle.getArea()) //12.56
const rectangle = new Rectangle(2, 3)
console.log(rectangle.getArea())// 6
八、接口的使用
1.声明对象类型
interface InfoType {
readonly name: string
age: number
friend?: {
name: string
}
}
const info: InfoType = {
name: 'yogln',
age: 18
}
console.log(info)//{ name: 'yogln', age: 18 }
2.定义对象的索引类型
// 通过interface来定义索引的类型
interface IndexLanguage {
[index: number]: string
}
const frontLange: IndexLanguage = {
[0]: 'vue',
[1]: 'react'
// [a]: 'html' //报错
// b: 'css' //报错
}
3.接口的继承
interface ISwim {
swimming: () => void
}
interface IFly {
flying: () => void
}
interface IAction extends ISwim, IFly {}
const action: IAction = {
swimming() {},
flying() {}
}
4.交叉类型
声明类型的时候
// 另一种组件类型的方式: 交叉类型
type NeverType = number & string
// 这其实是不可能存在的,所以,NeverType返回的类型是never
接口中的类型
interface Swim {
swimming: () => void
}
interface Fly {
flying: () => void
}
type MyType1 = Swim | Fly
type MyType2 = Swim & Fly
const obj1: MyType1 = {
flying() {}
}
const obj2: MyType2 = {
flying() {},
swimming() {}
}
当对象的返回类型是两个的时候,必须实现里面所有的方法
5.接口的实现
接口定义后,也是可以被类实现的:
如果被一个类实现,那么在之后需要传入接口的地方,都可以将这个类传入,这就是面向接口开发.
interface ISwim {
swimming: () => void
}
interface IFly {
flying: () => void
}
class Person implements ISwim, IFly {
swimming() {}
flying() {}
}
function swim(swim: ISwim) {
swim.swimming()
}
const p = new Person()
swim(p)
6.interface和type的区别
一般来说,非对象类型,建议使用type
,而对象类型type
和interface
都可以,那么我们怎们用呢?
interface IFoo {
name: string
}
interface IFoo {
age: 18
}
const foo: IFoo = {
name: 'yogln',
age: 18
}
可以看出interface
可以重复定义属性和方法,而且定义的属性都被合并在了一起,但是type
就不可以,而且type
的别名是不可以重复的,这就是他们的区别
7.字面量赋值
interface IPerson {
name: string;
age: number;
}
const info: IPerson = {
name: 'yogln',
age: 18,
address: 'zh' // 直接报错
}
我们发现info
对象里面比接口的定义多了一个address
就直接报错了,如果我们采用下面的方式赋值
interface IPerson {
name: string
age: number
}
const obj = {
name: 'yogln',
age: 18,
address: 'zh' // 不会报错
}
const info: IPerson = obj
console.log(info) // { name: 'yogln', age: 18, address: 'zh' }
这是为什么呢?
这是因为TypeScript
在字面量直接赋值的过程中,为了进行类型推导会进行严格的类型限制。 但是之后如果我们是将一个 变量标识符 赋值给其他的变量时,会进行freshness擦除操作。
8.枚举类型
enum Direction {
LEFT,
RIGHT,
TOP,
BOTTOM
}
function trunDirection(direction: Direction) {
switch (direction) {
case Direction.LEFT:
console.log('左转')
break
case Direction.RIGHT:
console.log('右转')
break
case Direction.TOP:
console.log('向上')
break
case Direction.BOTTOM:
console.log('向下')
break
default:
const foo: never = direction
break
}
}
9.枚举类型的值
枚举类型是有默认值的
enum Direction {
LEFT, // 0
RIGHT, // 1
TOP, // 2
BOTTOM //3
}
我们也可以为他们赋其他类型值
enum Direction {
LEFT = "LEFT",
RIGHT = "RIGHT",
TOP = "TOP",
BOTTOM = "BOTTOM"
}
我们也可以
let d: Direction = Direction.LEFT
九、认识泛型
1.泛型的定义方式
function sum<T>(arg: T) {
return arg
}
sum<number>(1)
sum<string>('abc')
sum<any[]>(['abc'])
2.泛型的参数类型
平时在开发中我们可能会看到一些常用的名称:
- T:Type的缩写,类型
- K、V:key和value的缩写,键值对
- E:Element的缩写,元素
- O:
Object
的缩写,对象
function foo<T, E, O>(arg1: T, arg2: E, arg3: O) {}
foo<number, string, boolean>(10, 'yogln', true)
3.泛型接口的使用
interface IPerson<T1 = string, T2 = number> {
name: T1
age: T2
}
const p: IPerson = {
name: 'yogln',
age: 18
}
4.泛型类的使用
class Person<T1, T2> {
x: T1
y: T2
constructor(x, y) {
this.x = x
this.y = y
}
}
const p: Person<string, number> = new Person('yogln', 18)
5.泛型的类型约束
想要打印具有length
的长度
interface ILength {
length: number
}
function printLength<T extends ILength>(arg: T) {
return arg.length
}
printLength('abcd')
printLength(['a', 'b', 'c'])
printLength({ length: 100 })
十、模块化
1.模块化开发
TypeScript两种方式支持我们的作用域
- 模块化:每个文件是一个独立的模块
- 命名空间:通过namespace来声明一个命名空间
当我们导出两个函数名称相同的函数的时候,是直接报错的
export function format() {
return 'timeFormat'
}
export function format() {
return 'priceFormat'
}
我们可以修改函数的名称不同,我们也可以使用namespace
export namespace time {
export function format() {
return 'timeFormat'
}
}
export namespace price {
export function format() {
return 'priceFormat'
}
}
导入的时候
import { time } from './utils/format'
console.log(time.format())
2.类型的查找
当我们在ts中使用
const imgEl = document.getElementById('image') as HTMLImageElement
大家是否会奇怪,我们的HTMLImageElement类型来自哪里呢?甚至是document为什么可以有getElementById的方 法呢?这里就涉及到typescript对类型的管理和查找规则
我们这里先给大家介绍另外的一种typescript文件:.d.ts文件,它是用来做类型的声明(declare)。 它仅仅用来做类型检测,告知typescript我们有哪些类型
那么typescript会在哪里查找我们的类型声明呢?
- 内置类型声明;
- 外部定义类型声明;
- 自己定义类型声明
内置类型声明
内置类型声明是typescript自带的、帮助我们内置了JavaScript运行时的一些标准化API的声明文件; 包括比如Math、Date等内置类型,也包括DOM API,比如Window、Document等; 内置类型声明通常在我们安装typescript的环境中会带有的
外部定义类型声明
外部类型声明通常是我们使用一些库(比如第三方库)时,需要的一些类型声明,这些库通常有两种类型声明方式:
-
方式一:在自己库中进行类型声明(编写.d.ts文件),比如axios
-
方式二:通过社区的一个公有库DefinitelyTyped存放类型声明文件
该库的GitHub地址:github.com/DefinitelyT…
该库查找声明安装方式的地址:www.typescriptlang.org/dt/search?s…
比如我们安装react的类型声明:
npm i @types/react --save-dev
比如我们安装了lodash
,我们想要使用,就可以在这个网站搜索进行安装相关的依赖
自己定义类型声明
什么情况下需要自己来定义声明文件呢?
- 情况一:我们使用的第三方库是一个纯的JavaScript库,没有对应的声明文件;比如lodash
- 情况二:我们给自己的代码中声明一些类型,方便在其他地方直接进行使用;
比如:我们在index.html
文件中定义了name
和age
,想在main.ts
中使用
<script>
let name = 'yogln'
let age = '18'
</script>
console.log(name)
console.log(age)
直接使用肯定是不行的,我们需要建一个.d.ts
文件,名字位置都可以随便,用来声明要使用的变量
declare let name: string
declare let age: number
这个时候就好了
声明函数声明类也是同样的道理
declare function foo(): void
declare class Person {
name: string
age: number
constructor(name: string, age: number)
}
声明模块lodash
declare module 'loadsh' {
export function join(args: any[]): any
}