目录
下文是根据王红元老师的学习笔记,并不作为ts学习
1.解析ts文件
1.运行ts文件
1.1方式一:安装ts-node
npm install ts-node -g
npm install tslib @types/node -g //ts-node需要依赖tslib和@types/node两个包
ts-node math.ts //运行ts文件
1.2方式二:webpack配置
1.2.1 包管理初始化
npm init
1.2.2 安装webpack和webpack-cli
npm install webpack webpack-cli -D
1.2.3 在package.json配置运行文件
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack"
}
1.2.4 配置webpack
const path = require('path')
module.exports = {
entry: './src/main.ts',
mode: 'development',
output: {
path: path.resolve(__dirname, './dist'),
filename: "bundle.js"
}
}
1.2.5下载ts解析
npm install ts-loader typescript -D
1.2.6初始化tsc(如果报错,下载方式一的两个包)
tsc --init
1.2.7配置webpack解析ts
resolve: {
extensions: [".ts", '.js'] //使用.ts模块化引入时,可以识别ts文件(会覆盖原有的)
}
1.2.8(下边搭建本地服务器,上述操作已经可以运行ts文件)
1.2.8.1下载webpack本地服务器
npm install webpack-dev-server -D
1.2.8.2配置热更新
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack",
"serve": "webpack serve"
}
1.2.8.3下载html模板
npm install html-webpack-plugin -D
1.2.8.4引入和使用模板
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: './src/main.ts',
mode: 'development',
output: {
path: path.resolve(__dirname, './dist'),
filename: "bundle.js"
},
resolve: {
extensions: [".ts", '.js'] //使用.ts模块化引入时,可以识别ts文件
},
module: {
rules: [
{
test: /\.ts$/,
loader: 'ts-loader'
}
]
},
plugins: [
new HtmlWebpackPlugin({ //热更新时嵌入ts的文件
template: './index.html'
})
]
}
2.数据类型
// 1.数据类型
// 1.1数组类型
let strArr: string[] = ['abc', 'cbd']
strArr.push("123")
const myObj: object = {
name: '小红',
age: 18
}
// console.log(myObj.name) //报错不存在该值
// myObj.name = '小明' //报错不存在当前属性
// 1.1 当数值为null或者undefined时不要用类型推断,会被推断成any
let u: undefined = undefined
let n: null = null
// 1.2 symbol定义相同属性名称
let s1: symbol = Symbol('title')
let s2: symbol = Symbol('title')
const youObj: object = { //这边要用[],不加是当字符串处理的,取不到上边定义的是s1和s2
[s1]: '我的家乡',
[s2]: '我的村庄'
}
console.log('Symbol', youObj)
// 1.3any类型
let arrAny: any[] = [1.22, '小红', { name: '小红' }]
// 1.4unknow类型 用于描述类型不确定的变量
let k: unknown
function logSomedata() {
if (Math.random() > 0.5) {
return 'str'
} else {
return 123
}
}
k = logSomedata()
console.log('unknown', k)
// 1.5void类型
function includeVoid(): void {
console.log('返回值包含了void')
}
// 1.6返回值不包含void(默认)
function exculdeVoid() {
console.log('返回值不包含了void')
}
let v1 = includeVoid()
let v2 = exculdeVoid()
console.log('明确了void', v1, '没有指定void', v2) //v1 和 v2 都是undefined
// 1.7never
// never 是其它类型(包括 null 和 undefined)的子类型,代表从不会出现的值。
// 这意味着声明为 never 类型的变量只能被 never 类型所赋值
function looping(): never { //1.7.1死循环
while (true) {
console.log('123')
}
}
function looping2(): never { //1.7.2抛出一个错误 never 类型可以赋值给 never类型
throw new Error('返回值是never')
}
function looping3(message: number | string) {
switch (typeof message) {
case 'string':
console.log('foo')
break
case 'number':
console.log('bar')
break
default:
const check: never = message
}
}
// --------菜鸟教程never
let x: never;
let y: number;
// 编译错误,数字类型不能转为 never 类型
// x = 123;
// 运行正确,never 类型可以赋值给 never类型
x = (() => { throw new Error('exception') })();
// 运行正确,never 类型可以赋值给 数字类型
y = (() => { throw new Error('exception') })();
// 返回值为 never 的函数可以是抛出异常的情况
function error(message: string): never {
throw new Error(message);
}
// 返回值为 never 的函数可以是无法被执行到的终止点的情况
function loop(): never {
while (true) { }
}
// -------菜鸟教程never
// 1.8tuple元祖每个元素都有自己特性的类型
const t1: [number, string, number] = [1, 'a', 3]
const t2: (number | string)[] = [1, 2, 3, 'b'] //如果写成[number|string]是只有一个元素(元素可以是number或者是string)
3.函数类型和断言
// 2.函数
let arr: (string | number)[] = ['abc', 'cbb', 123]
arr.forEach(item => { //item并没有指定类型,但是ts会 “上下文类型” 自动判断
console.log(item)
})
// 2.1对象类型 + 可选类型
function objfunc(point: { x: number, y: number, z?: string }): number {
if (point.z) {
console.log('你传入了z属性' + point.z)
//如果不加if并且传入了z,此时打印出来的就是undefined,因为可选类型就是类型和undefined的联合类型
}
return point.x ** point.y
}
console.log('2的3次方为', objfunc({ x: 2, y: 3 }))
// 2.2联合类型 + 缩小类型
function unionType(x: number | string) {
if (typeof x === "number") {
console.log("number", x * 2)
} else {
console.log("string", x.toUpperCase())
}
}
unionType(2)
unionType('abc')
// 2.3类型别名
type obj1 = {
x: number,
y: number
}
type ali1 = number | string
function objfunc2(obj: obj1) {
return obj.x * obj.y
}
console.log('类型别名', objfunc2({ x: 3, y: 4 }))
// 2.4断言(断言用于转化为具体或者不太具体的类型)+ 非空断言!.
// 断言只是处理了大多数的编译不会报错,只是程序员的个人判定,控制台仍然会报错
const myImg = document.querySelector('.img') as HTMLImageElement //如果没有断言,访问不了src属性
// myImg.src = './a.jpg'
const test = ("abc" as unknown) as number //abc值无法直接转为number的
console.log(test)
class Person {
}
class Student extends Person {
studying() {
console.log('学生在学习')
}
}
function useStudying(p: Person) {
(p as Student).studying() //断言用于缩小范围,不然是拿不到这个方法的(让编译以为p为Student)
}
const stu = new Student()
useStudying(stu)
function noEmpityAs(str?: string) {
// console.log(str.toUpperCase()) //编译报错,会出现undefined现象
console.log(str!.toUpperCase()) //这样做,只是编译不会报错,但是不传值时控制台会报错(让编译以为这是个必传的值)
}
noEmpityAs('abcde')
4.可选链-逻辑符号-字面量类型
// 3.可选链
type info = {
name: string,
age: number,
sex: string,
children?: {
name?: string,
age: number,
sex: string,
}
}
const ming: info = {
name: '小明',
age: 16,
sex: '男',
}
console.log(ming?.children?.name) //一般只用于取值而不是赋值
// 4.!! + ?? 逻辑或||的本质是if语句的判断
let message = ""
console.log(!!message) //相当于Boolean,将类型转化为boolean类型
console.log(message ?? 123) //当??左侧为null或者undefined时返回右侧操作数,否则为左侧,这里打印""
// 5.字面量类型
// 5.1 字面量类型和联合类型一起使用才有意义
type direction = "right" | "left" | "top"
function move(d: direction) {
switch (d) {
case "right":
console.log("right")
break
case "left":
console.log("left")
break
case "top":
console.log("top")
default:
console.log("error")
break
}
}
move("right")
// 5.2字面量推理
const config = {
url: 'http://www.baidu.cn',
method: "get"
}
function request(url: string, method: "get" | "post") {
console.log(url, "发送了", method, "请求")
}
// ruquest(config.url, config.method) //报错:类型“string”的参数不能赋给类型“"get" | "post"”的参数
request(config.url, config.method as "get") //方法一:断言
const config1 = {
url: 'http://www.baidu.cn',
method: "get"
} as const //方法二:直接转化为字面量类型,而不是string类型
request(config1.url, config1.method)
type Method = "get" | "post" //方法三:编写一个字面量类型
function request1(url: string, method: Method) {
console.log(url, "发送了", method, "请求")
}
request1(config.url, config.method as Method)
5.类型缩小
// 6.类型缩小
// 6.1typeof
type ID = number | string
function unionPd(id: ID) {
if (typeof id === "string") {
console.log('string')
} else {
console.log('number')
}
}
// 6.2switch === == !== !=
type Direction = "right" | "left" | "top"
function Move(d: Direction) {
switch (d) {
case "right":
console.log("right")
break
case "left":
console.log("left")
break
case "top":
console.log("top")
default:
console.log("error")
break
}
}
Move("right")
// 6.3instance of
function isDate(date: Date | string) {
if (date instanceof Date) {
console.log(date.toLocaleTimeString())
} else {
console.log(date.toString())
}
}
// 6.4in 运算符,通常用于检测对象里的属性和方法
type Fish = { swim: () => void } //字面量类型
type dog = { run: () => void } //字面量类型
function sport(animal: Fish | dog) {
if ('swim' in animal) {
animal.swim()
} else {
animal.run()
}
}
const fish: Fish = {
swim() {
console.log('鱼在游泳')
}
}
sport(fish)
6.函数参数和this绑定
// 7.函数详解
// 7.1 void + ?(可选参数) + 默认参数=
function add(num1: number, num2?: number, num3: number = 6) {
if (num2) {
if (num3) {
return num1 + num2 + num3
} else {
return num1 + num2
}
} else {
return num1
}
}
console.log(add(2, 6, 3))
// 7.2剩余参数
function mul(...num: number[]) {
let total: number = 1
for (const item of num) {
total *= item
}
return total
}
console.log(mul(3, 4, 5))
// 7.3this指向
// 7.3.1this指向不明确
function sayHello() {
// console.log('obj调用了', this.name) //报错this是拿不到的
}
const obj = {
name: 'obj',
sayHello
}
obj.sayHello()
// 7.3.2明确this指向
type thisType = {
name: string
}
function sayHi(this: thisType, message: string) {
console.log(this.name + message)
}
const obj2 = {
name: 'obj2',
sayHi
}
// 隐式绑定
obj2.sayHi('Hi Hi')
// 显示绑定
sayHi.call({ name: 'obj3' }, 'Hi Hi Hi')
sayHi.apply({ name: 'obj4' }, ['Hi Hi Hi Hi'])
7.重载和联合类型
// 7.4函数重载
// 7.4.1 参数类型混乱
// function sum(num1: number | string, num2: number | string): string | number {
// return num1 + num2 //运算符“+”不能应用于类型“string | number”和“string | number”
// }
function sum(num1: string, num2: string): string
function sum(num1: number, num2: number): number
function sum(num1: any, num2: any): any {
return num1 + num2
}
sum(1, 2)
sum('a', 'b')
// sum(true, false) 报错
// 7.4.2联合类型和重载(开发时尽量使用联合类型而不是重载)
function getLength(data: string | any[]) {
return data.length
}
getLength('abc')
getLength([1, 2, 'ab'])
function getLength1(data: string): number
function getLength1(data: any[]): number
function getLength1(data: any): number {
return data.length
}
8.类
// 8.类
class Person {
name!: string //加上!就可以在constructor中不用明确赋值
age: number
constructor(name: string, age: number) {
// this.name = name
this.age = age
}
swim() {
console.log(this.name, this.age, '---swim---') //如果没有在构造函数赋值这边就是undefined
}
run() {
console.log(this.name, this.age, '---run---')
}
}
// 8.1类的继承
class Student extends Person {
sno: string
constructor(name: string, age: number, sno: string) {
super(name, age)
this.sno = sno
}
swim() { //8.2重写父类的swim
super.swim() //可以通过super调用父类方法
console.log('son swimming' + this.sno)
}
}
const p = new Person('xiaohong', 12)
const s = new Student('xiaolan', 13, '012')
p.run()
s.swim()
// 8.3多态
class Animal {
action() {
console.log('--animal--')
}
}
class dog extends Animal {
action(): void {
console.log('--dog--')
}
}
class fish extends Animal {
action(): void {
console.log('--fish--')
}
}
function makeAction(animals: Animal[]) {
animals.forEach(item => {
item.action()
})
}
makeAction([new dog(), new fish()])
// 8.4类的成员修饰符
class Figure {
private x: number //private -- 同一类可见(子类也拿不到h78)
protected y: number //protected -- 同一类或子类
constructor(x: number, y: number) {
this.x = x
this.y = y
}
getX() {
return this.x
}
}
class Rect extends Figure {
readonly z: number //不允许修改,但是可以在构造函数里赋值
readonly re?: Rect
d: number
constructor(x: number, y: number, z: number, d: number, re?: Rect) {
super(x, y)
this.z = z
this.d = d
this.re = re
}
getF() {
// return this.x //报错(super.x也是一样报错)
}
getY() {
return this.y
}
}
const f = new Figure(10, 20)
// f.x 报错--私有属性实例外部是拿不到的
console.log(f.getX()) //封装了getX方法来拿到内部的属性
// console.log(f.y) //报错外部是拿不到的
const r = new Rect(10, 20, 30, 20, new Rect(10, 20, 30, 40))
// r.z = 40 //只读属性不允许修改
if (r.re) {
r.re.d = 10 //虽然re不可修改,但是re里的d是可修改的,理论类似于对象
}
// 8.5getter和setter + 静态成员
class tool {
private _name: string //规范私有变量一般以下划线开头
static tn: number = 10 //通过类名来访问修改,构造函数也不需要赋值(上下文是查找不到t的)
constructor(name: string) {
this._name = name
}
get name() {
return this._name
}
set name(newName) {
this._name = newName
}
getName() {
return this._name
}
static sGetName() {
// return this._name //报错,static方法访问不了内部属性
}
}
const t = new tool('hairui')
t.name = "hairui view"
console.log(t.name, t.getName())
console.log(tool.tn)
console.log(tool.sGetName)
abstract class Shape {
abstract calcWidth(): number
}
class Circle extends Shape {
r: number
constructor(r: number) {
super()
this.r = r
}
calcWidth(): number {
return 3.14 * this.r ** 2
}
}
const c = new Circle(2)
console.log(c.calcWidth())
// 8.6类的类型
class Demo {
name: string
constructor(name: string) {
this.name = name
}
}
const d = new Demo('xiaolan')
const d1: Demo = {
name: 'xiaohong'
}
export { }
9.接口
// 9.接口
// 9.1定义接口
interface Point {
name: string
age?: number
readonly friends: {
name: string
}
}
interface indexLanguage {
[index: number]: string
}
const index: indexLanguage = {
1: "java",
2: "js",
// 3: 12 //报错不能将string赋给number
}
interface calcWidth1 {
(num1: number, num2: number): number
}
interface calcWidth2 {
calc: (num1: number, num2: number) => number //可以两种声明方法只允许一个:(个人)
}
const c1: calcWidth1 = (num1, num2) => num1 + num2
const c2: calcWidth2 = {
calc: (num1, num2) => num1 + num2
}
// 9.2接口的多继承
interface Animal {
swimming: () => void
}
interface Person {
name: string
running: () => void
}
interface Student extends Animal, Person {
sno: string
}
const s: Student = {
name: 'code',
sno: '1102',
swimming() { },
running() { }
}
// 9.3类继承接口--面向接口编程
class Teacher implements Animal, Person {
name: string
constructor(name: string) {
this.name = name
}
swimming() {
console.log('swimming' + this.name)
}
running() { }
}
function swim(swimmer: Animal) {
swimmer.swimming()
}
const t = new Teacher('ter')
swim(t)
// 9.4交叉类型 不可能有属性既是string又是number,所以这个myType是never
type myType = string & number
// 以上是没有什么意义的,所以通常我们会对对象进行交叉
interface runIn {
run: () => void
}
interface swimIn {
swim: () => void
}
type allType1 = runIn & swimIn //使用该属性后两个方法都要实现
type allType2 = runIn | swimIn //使用该属性后两个方法实现一个即可
const allInter1: allType1 = {
run() { },
swim() { }
}
const allInter2: allType2 = {
run() { },
// swim() { }
}
// type和interface的区别
type one = {
name: string
}
// type one = { //报错上边定义过one了
// age: 12
// }
interface two {
name: string
}
interface two {
age: number
}
const twoS: two = {
name: 'three',
age: 12
}
// 9.5擦除操作和字面量赋值
interface Book {
name: string,
eating: () => void
}
const book: Book = {
name: '小红',
eating() {
},
// age: 12 //报错(类型推导会进行严格的类型限制)
}
const book2 = {
name: '小红',
eating() {
},
age: 12
}
const book1: Book = book2 //这样并没有报错(擦除操作)
console.log(book1) //并且该属性存在age,但是用book1.age编译会报错
// 因此定义非对象建议用type 比如Direction Alignment,一些Function
export { }
10.枚举与泛型
// 10.枚举
// 10.1增强阅读性
enum Direction {
Left,
Right,
Top,
Bottom
}
function turn(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:
break
}
}
turn(Direction.Left) //阅读性较强
// 10.2枚举类型的值
enum Direction2 { //如果我们没有赋值,默认就是1234,如果第一个是100,那么依次递增101.102.103
Left = 1,
Right, //2
Top = 3,
Bottom = 'abc'
}
console.log(Direction2)//{1: 'Left', 2: 'Right', 3: 'Top', Left: 1, Right: 2, Top: 3, Bottom: 'abc'}
// (个人)只要是数字,就添加一个数组项的,可以通过Direction2[1]或Direction2["1"]获取
// 11.泛型
// 11.1泛型的基本用法
function foo<Type>(args: Type) {
return args
}
foo('abc') //这里类型推导会判断是字面量类型,也是适用的
foo<number>(123) //类似于让用户决定输出的类型是啥
// 11.2传递多个参数
function foo1<T, E>(a1: T, a2: E) {
if (typeof a1 === "string" && Array.isArray(a2)) { //下边的11.6方法更便捷
return a1.length + a2.length
}
}
console.log(foo1('abc', [1, 2, 3]))
// 11.3泛型接口 + 默认值
interface Person<T, N = number> { //泛型是没有额理性推导的,但是有默认值
name: T,
age: T[],
handle: (value: T) => T
}
const p: Person<number> = {
name: 105,
age: [12, 13],
handle: (value: number) => 12
}
// 11.4泛型类
class Animal<T> {
name: T
cno: T
constructor(name: T, cno: T) {
this.name = name
this.cno = cno
}
eatting() {
console.log(this.name + '的编号为' + this.cno)
}
}
const a: Animal<string> = {
name: '老虎',
cno: "001",
eatting() {
console.log(this.name + '的编号为' + this.cno)
}
}
const a1 = new Animal<string>('兔子', '002')
const a2 = new Animal('狮子', '003') //类型推导
a.eatting()
// 11.5泛型在数组的应用
const arr1: string[] = ['13', '12']
const arr2: Array<string> = ['a', 'b']
// 11.6泛型约束
interface ownLength {
length: number //声明一个接口说明该对象是拥有length属性的
}
function getLength<T extends ownLength>(params: T) {
return params.length
}
getLength([1, 2])
getLength('123')
// getLength(10) //编译报错,数字没有length属性
export { }
11.模块化开发
// 12.ts补充
// 12.1模块化开发
// 12.1 commonjs和namespace
import { sum } from './math'
import { space1 } from './math'
console.log(sum(10, 20))
console.log(space1.sub(20, 10))
const img = document.querySelector('.img') as HTMLImageElement //问:那么HTMLImageElement从哪里来喃?
// https://github.com/microsoft/TypeScript/tree/main/lib 答:ts自带的在.d.ts文件里做了声明
// console.log(img.src)
// 12.2外部文件声明
// 12.2.1方法一借助外部声明
// https://www.typescriptlang.org/dt/search/?search=
// https://www.typescriptlang.org/dt/search/?search=lodash(以lodash为例)
// 在上边的网址可以找到两条命令,一条是安装lodash,一条是lodash的声明文件
// npm i lodash npm i @types/lodash --save-dev
// import lodash from 'lodash'
// console.log(lodash.join([1, 2, 3]));
// 12.2.2方法二自己声明模块
import lodash from 'lodash' //默认是从node_modules找到hairui文件,但是找不到hairui
console.log(lodash.join([2, 6, 4])); //这样做的意义是避免lodash没有声明报错(相当于省略了npm i @types/lodash --save-dev)
// 但是这样写的话,用一个方法,就要写一个方法,很麻烦
declare module 'lodash' { //这边要加''
export function join(arr: any[]) {
return arr.join(' ')
}
}
export function sum(num1: number, num2: number) {
return num1 + num2
}
export namespace space1 {
export function sub(num1: number, num2: number) { //函数和命名空间都要导出
return num1 - num2
}
}