TypeScript基础

一、TypeScript 简介:

1、TypeScript 包含了 javaScript 的所有内容。

2、新增了静态类型检查、接口、泛型,更适合大型项目的开发。

3、TypeScript 需要编译为 javaScript

二、javaScript 的缺陷:

1、数据类型不明确

2、逻辑有问题,不会报错

3、访问不存在的属性,不会报错

4、低级的拼写错误,不会报错

三、编译 TypeScript

全局安装 TypeScript : npm i typescript -g

1、命令行编译:

使用 tsc 命令编译 ts 文件:tsc ts文件名

① 可能会报错:

② 原因:node版本太低了。

③ 解决办法:

node版本 切换到 14.0以上(我用的 14.21.3 版本)

④ node版本查询:https://nodejs.p2hp.com/download/

2、自动化编译:

① 生成 tsconfig.json 文件:tsc --init

② 监视某个 ts 文件:tsc --watch ts文件名

③ 监视所有 ts 文件: tsc --watch

当编译出错时,不生成 js 文件

方法一:tsconfig.json 文件修改配置 "noEmitOnError": true,

方法二:执行命令 tsc noEmitOnError --watch

四、TypeScript 类型介绍:

1、包含js 中所有的数据类型:

string

(1)stringString 类型的区别:
typescript 复制代码
let a:string = '小红'
// 报错:不能将类型"String"分配给类型"string"。
//   "string"是基元,但"String"是包装器对象。
a = new String("小新")

let b:String = '小米'
b = new String("小兰")

number

(1)numberNumber 类型的区别:
typescript 复制代码
let a:number = 333
// 报错:不能将类型"Number"分配给类型"number"。
//   "number"是基元,但"Number"是包装器对象。
a = new Number(444)

let b:Number = 111
b = new Number(222)

boolean

(1)booleanBoolean 类型的区别:
typescript 复制代码
let a:boolean = true
// 报错:不能将类型"Boolean"分配给类型"boolean"。
//   "boolean"是基元,但"Boolean"是包装器对象。
a = new Boolean(false)

let b:Boolean = false
b = new Boolean(true)

null

undefined

symbol

bigint

object

包含:ArrayFunctionDateError

(1)objectObject 类型的区别:
a、object 能存储【非原始类型】的数据
typescript 复制代码
let a:object
a = {};
a = { name: '小兰' };
a = [1, 3, 5, 7, 9];
a = function(){};
a = new String('小笼包');
class Person{};
a = new Person();

a = 1; // 报错:不能将类型"number"分配给类型"object"。
a = true; // 报错:不能将类型"boolean"分配给类型"object"。
a = '小红'; // 报错:不能将类型"string"分配给类型"object"。 
a = null; // 报错:不能将类型"null"分配给类型"object"。
a = undefined; // 报错:不能将类型"undefined"分配给类型"object"。
b、Object 能存储【可以调用Object方法】的数据:

除了nullundefined之外的所有数据

(2)声明对象类型:

? 表示可选属性

a. 声明对象类型的三种方式:
typescript 复制代码
// 使用逗号分隔
let person: { name: string, age?:number }
person = {
    name: '小红',
    age: 18
}
// 使用换行进行分隔
let animal: {
    name: string
    gender:string 
}
animal = {
    name: '卡皮巴拉',
    gender: '公'
}
// 使用分号进行分隔
let company: { name: string; type:string }
company = {
    name: '世大强',
    type: '游戏'
}
b. 索引签名:允许定义任意数量的对象属性,适用于定义动态属性的对象
typescript 复制代码
let person: { 
    name: string, 
    age?:number, 
    gender: string,
    [key:string]: any
}

person = {
    name: '小红',
    gender: '女',
    hobby: '打游戏'
}
(3)声明函数类型:
a、常规声明函数:
typescript 复制代码
// 声明了一个 有两个参数是 number 类型、返回值也是 number 类型的函数
let func: (a: number,b:number) => number

func = (x,y) => {
    return x+y
}

func = function(x,y) {
    return x+y
}
b、使用自定义类型声明函数:

转 3、自定义类型的方式

(4)声明数组类型:
a、常规声明数组:
typescript 复制代码
let arr:string[]
arr = ['小红', '小明']
b、使用泛型声明数组:
typescript 复制代码
let arr:Array<string>
arr = ['小红', '小明']

2、新增的数据类型:

any:任意类型

any类型的变量,可以赋值给任意类型的变量,可以读取变量的任何属性

(1)定义方式一:let 变量名
typescript 复制代码
let a
a = 11
a = true
a = '小明'

let b:string
b = a   // 因为 a最后是string类型,b也是string类型,所以可以赋值成功

// ----------------------分割线---------------------
let c
c = 22
c = '小红'
c = false

let d:string
// 会报错:不能将类型"boolean"分配给类型"string"。
d = c   // 因为 c最后是boolean类型,b是string类型,所以不可以赋值成功
(2)定义方式二:let 变量名:any
typescript 复制代码
let a:any
a = 11
a = '小明'
a = true

a.toUpperCase() 

let b:string
b = a

unknown:未知类型

(1)unknown 是类型安全的 any,适用于 不确定数据的具体类型

unknown类型的变量,不可以赋值给任意类型的变量,也不可以读取变量的任何属性

typescript 复制代码
let a:unknown
a = 1
a = true
a = '小兰'


let b:string
// 报错:不能将类型"unknown"分配给类型"string"。
b = a 

如果想要unknown类型的变量,赋值给某类型的变量或者读取某类型变量的属性,可使用如下方式:

a.方式一:做类型检查
typescript 复制代码
let a:unknown
a = 1
a = true
a = '小兰'

// 报错:"a"的类型为"未知"。
// a.toUpperCase()   

// 读取某类型变量的属性方式一:
if (typeof(a) ===  'string') {
    a.toUpperCase()
}

let b:string
// 赋值给某类型的变量方式一:
if (typeof(a) ===  'string') {
    b = a
}
b.方式二:断言
typescript 复制代码
let a:unknown
a = 1
a = true
a = '小兰'

let b:string
// 方式二
b = a as string
c.方式三:断言
typescript 复制代码
let a:unknown
a = 1
a = true
a = '小兰'

let b:string
// 方式三
b = <string>a

never:不能有值,undefined''0null 都不行!

不是限制变量的,是限制函数的返回类型的

typescript 复制代码
function test():never {
    throw new Error("程序异常")
}
test()

void:限制函数的返回值声明

(1)函数不返回任何值,调用者也不应该依赖其返回值进行任何操作
typescript 复制代码
function test3():void {
    console.log('@')
}

let result  = test3()
console.log(result)
// 报错:无法测试 "void" 类型的表达式的真实性。
if(result) { // 依赖返回值进行操作

}
(2) undefinedvoid 可以接受的一种
typescript 复制代码
// 无警告
function test():void {
    return undefined
}
// 无警告
function test2():void {
    return
}
// 无警告
function test3():void {
}

tuple:元组,一种特殊的数组类型,可以存储固定数量的元素。

元组中的每个元素的类型都是已知的,且类型可以不同。

typescript 复制代码
// ? 表示此类型的元素可选
let arr:[number,string,boolean?]
arr = [1,'小红', true]

// ... 表示可以有0-n个此类型的元素
let arr2:[number,string,boolean?, ...string[]]
arr2 = [1,'小红', true, '小花', '小明']

enum:枚举,定义一组命名常量

可以增强代码的可读性,也可以让代码更好维护。

(1)数字枚举:
typescript 复制代码
enum AirMode {
    cool,
    hot,
    dry,
    auto
}

console.log(AirMode) // {0: 'cool', 1: 'hot', 2: 'dry', 3: 'auto', cool: 0, hot: 1, dry: 2, auto: 3}

function getMode(data:AirMode) {
    if (data === AirMode.cool) {
        console.log('制冷模式')
    } else if (data === AirMode.hot) {
        console.log('制热模式')
    } else if (data === AirMode.dry) {
        console.log('除湿模式')
    } else if (data === AirMode.auto) {
        console.log('自动模式')
    } else {
        console.log('暂无模式')
    }
}

getMode(AirMode.auto)
console.log(AirMode[0]) // cool


可以自定义递增的数字:

typescript 复制代码
enum AirMode {
    cool = 2,
    hot,
    dry,
    auto
}

console.log(AirMode) // {2: 'cool', 3: 'hot', 4: 'dry', 5: 'auto', cool: 2, hot: 3, dry: 4, auto: 5}
(2)字符串枚举:

会丢失反向映射

typescript 复制代码
enum AirMode {
    Cool = "cool",
    Hot = "hot",
    Dry = "dry",
    Auto = "auto"
}

console.log(AirMode) // {Cool: 'cool', Hot: 'hot', Dry: 'dry', Auto: 'auto'}
(3)常量枚举:使用const关键字定义,在编译时会被 内联

所谓"内联"其实就是 TypeScript 在编译时,会将枚举成员引用 替换为它们的实际值,而不是生成额外的枚举对象。

内联的好处:减少生成的 JavaScript 代码量,并提高运行时性能。

普通枚举:

常量枚举:

3、自定义类型的方式:

type:为任意类型创建别名

(1)基本用法:
a、定义对象
typescript 复制代码
// 定义 Box 类型
type Box = {
    width: number,
    height: number
}

let pinkBox:Box = {
    width: 20,
    height: 30
}
b、定义类
typescript 复制代码
type Box = {
    width: number,
    height: number
}

class area implements Box {
    constructor(
        public width:number,
        public height: number
    ){

    }
    calc(color: string) {
        console.log(`我有一个${color}房子,面积是${this.width * this.height}平方米。`)
    }
}

let a1 = new area(12, 10)
a1.calc('红') // 我有一个红房子,面积是120平方米。
c、定义函数
typescript 复制代码
type func = (width: number,height: number) => void

let a1:func = (width: number,height: number) => {
    console.log(`面积是${width*height}平方米。`)
}
a1(12,10) // 面积是120平方米。
(2)联合类型:|

一个值的类型可以是多个类型中的一个

typescript 复制代码
type key = string | number
let a:key = 111

type Gender = '男' | '女'


let getGender:(value:Gender) => Gender
getGender = (value) => {
    console.log(value)
    return value
}
getGender('女')
(3)交叉类型:&

一个值的类型是由多个类型合并得出的。常用于对象类型

typescript 复制代码
type Size = {
    width: number,
    height: number
}

type Style = {
    color: string,
    border: number,
    radius: number
}


let box: Size & Style = {
    width: 200,
    height: 400,
    color: 'pink',
    border: 2,
    radius: 8,
}
(4)特殊情况:void

使用类型声明 限制函数返回值为 void 时,TypeScript 并不会严格要求返回值为空。

官网链接:https://www.typescriptlang.org/docs/handbook/2/functions.html#assignability-of-functions

typescript 复制代码
type specialFunc = () => void

let func1:specialFunc = () => {
    // 允许返回非空类型
    return 123
}

let res = func1()
// 报错:无法测试 "void" 类型的表达式的真实性。
// if (res) {}

interface:接口

请移步七、接口

五、属性修饰符:

1、public:公开的

① 被 类内部 访问:

typescript 复制代码
class Person {
    public name: string;
    public age: number;
    constructor(name:string, age: number) {
        this.name = name
        this.age = age
    }
    public say() {
        // 在类的内部使用
        console.log(`我的名字是${this.name},我今年${this.age}岁了。`)
    }
}

② 被 子类 访问(内部外部皆可):

typescript 复制代码
class Person {
    public name: string;
    public age: number;
    constructor(name:string, age: number) {
        this.name = name
        this.age = age
    }
    public say() {
        console.log(`我的名字是${this.name},我今年${this.age}岁了。`)
    }
}
// 在子类使用
class Student extends Person {
    grade: string;
    constructor(name: string,age: number, grade: string) {
        super(name,age)
        this.grade = grade
    }

    override say() {
    	// 在子类内部访问
        console.log(`我的名字是${this.name},我今年${this.age}岁了,我在${this.grade}年级学习。`)
    }
}

// 在子类外部访问
let s1 = new Student('小蓝', 10, '二')
console.log(s1.name) // 小蓝
console.log(s1.age) // 10
s1.say() // 我的名字是小蓝,我今年10岁了,我在二年级学习。

③ 被 类外部 访问:

typescript 复制代码
class Person {
    public name: string;
    public age: number;
    constructor(name:string, age: number) {
        this.name = name
        this.age = age
    }
    public say() {
        console.log(`我的名字是${this.name},我今年${this.age}岁了。`)
    }
}

// 在类的外部使用
let p1 = new Person('小红', 18)
console.log(p1.name) // 小红
console.log(p1.age) // 18
s1.say() // 我的名字是小红,我今年18岁了。

2、属性的简写形式:

typescript 复制代码
class Person {
    constructor(public name:string, public age: number) {}
    public say() {
        // 在类的内部使用
        console.log(`我的名字是${this.name},我今年${this.age}岁了。`)
    }
}

3、protected:受保护的

① 被 类内部 访问:

typescript 复制代码
class Person {
	constructor(
		protected name:string, 
		protected age: number
	) {}
    protected say() {
        // 在类内部使用
        console.log(`我的名字是${this.name},我今年${this.age}岁了。`)
    }
}

② 被 子类内部 访问:

typescript 复制代码
class Person {
    constructor(
		protected name:string, 
		protected age: number
	) {}
    protected say() {
        // 在类内部使用
        console.log(`我的名字是${this.name},我今年${this.age}岁了。`)
    }
}
// 在子类使用
class Student extends Person {
    constructor(
        name: string,
        age: number, 
        public grade: string
    ) {
        super(name,age)
    }

    override say() {
        console.log(`我的名字是${this.name},我今年${this.age}岁了,我在${this.grade}年级学习。`)
    }
}
let s1 = new Student('小蓝', 10, '二')
// console.log(s1.name) // 报错 -- 属性"name"受保护,只能在类"Person"及其子类中访问。
s1.say() // 我的名字是小蓝,我今年10岁了,我在二年级学习。

4、private:私有的

① 被 类内部 访问:

typescript 复制代码
class Person {
    constructor(private name:string, private age: number) { }
    private say() {
        // 在类内部使用
        console.log(`我的名字是${this.name},我今年${this.age}岁了。`)
    }
}
class Student extends Person {
    constructor(
        name: string,
        age: number, 
        public grade: string
    ) {
        super(name,age)
    }

    private speak() {
        // 报错:属性"name"为私有属性,只能在类"Person"中访问。
        // console.log(`我的名字是${this.name},我今年${this.age}岁了,我在${this.grade}年级学习。`)
    }
}
let s1 = new Student('小蓝', 10, '二')
console.log(s1) // Student {name: '小蓝', age: 10, grade: '二'}
// console.log(s1.name) // 报错 -- 属性"name"为私有属性,只能在类"Person"中访问。

5、readonly:只读属性,属性无法修改

typescript 复制代码
class Person {
    constructor(public readonly name:string, public age: number) {}
    public say() {
        // 在类的内部使用
        console.log(`我的名字是${this.name},我今年${this.age}岁了。`)
    }
}
let p1 = new Person('小红', 18)
console.log(p1) // Person {name: '小红', age: 18}
// p1.name = '小兰' // 报错:无法为"name"赋值,因为它是只读属性。
p1.age = 20
console.log(p1) /// Person {name: '小红', age: 20}

六、抽象类:

1、定义:

① 抽象类无法被实例化:

不能用 new 关键字去调用,可以被继承

② 抽象类专门用来定义类的结构和行为:

定义通用的属性和方法

③ 抽象类中可以写抽象方法:

定义抽象方法的名字、入参类型和返回值类型

④ 抽象类中可以写具体的实现:

定义具体方法,在具体方法中使用 抽象方法中的属性 / 抽象方法 来计算或者输出数据

⑤ 为派生类(子类)提供一个基础结构:

子类共享抽象类中的部分代码

⑥ 派生类(子类)必须实现抽象类的抽象方法:

子类必须定义一个和抽象类中抽象方法同名的方法,并且要定义该方法的返回值

2、使用场景:

① 定义通用接口:

为一组相关的类定义通用的行为(方法或属性)时。

② 提供基础实现:

在抽象类中提供某些方法或为其提供基础实现,这样派生类就可以继承这些实现。

③ 确保关键实现:

强制派生类实现一些关键行为。

④ 共享代码和逻辑:

当多个类需要共享部分代码时,抽象类可以避免代码重复。

3、具体实现:

typescript 复制代码
abstract class Person {
    // 构造方法
    constructor(public name:string, public age: number) { }
    // 抽象方法 --关键实现
    abstract hobby(): string
    // 具体方法 -- 通用接口、基础实现
    speak() {
        console.log(`我叫${this.name},我喜欢玩${this.hobby()}`)
    }
}
class Player extends Person {
    constructor(name:string, age: number, private hobbyName:string) {
        super(name, age)
    }
    hobby():string {
        if (this.age > 18) {
            return this.hobbyName
        } else {
            return `${this.hobbyName},但是我也要努力学习。`
        }
        
    }
}
let p1 = new Player('小红', 20, '《魔兽世界》')
p1.speak() // 我叫小红,我喜欢玩《魔兽世界》
let p2 = new Player('小兰', 15, '《蛋仔派对》')
p2.speak() // 我叫小兰,我喜欢玩《蛋仔派对》,但是我也要努力学习。

七、接口:interface

1、解释:

interface 主要用来定义对象函数 的结构,不能包含实现!

2、优点:

可以保证代码的一致性和类型安全

3、使用:

① 定义结构:

使用 implements 实现接口

typescript 复制代码
// 定义接口
interface PersonInterface {
    name:string;
    age: number;
    introduce: (grade:string) => void
}
// 类 实现 接口
class Student implements PersonInterface {
    constructor(
        public name: string,
        public age: number
    ) {

    }
    // introduce(grade: string) {
    //     console.log(`我的名字是${this.name},我今年${this.age}岁,上${grade}年级。`)
    // }
    introduce = (grade: string) => {
        console.log(`我的名字是${this.name},我今年${this.age}岁,上${grade}年级。`)
    }
}

let s1 = new Student('小红', 8)
s1.introduce('二') // 我的名字是小红,我今年8岁,上二年级。

② 定义对象结构:

typescript 复制代码
// 定义接口
interface PersonInterface {
    name:string;
    age: number;
    introduce: (grade:string) => void
}

let student: PersonInterface = { // 对象的类型是某个接口
    name: '小红',
    age: 8,
    introduce: (grade:string) => {
        console.log(`我的名字是${student.name},我今年${student.age}岁,上${grade}年级。`)
    }
}
student.introduce('二') // 我的名字是小红,我今年8岁,上二年级。

③ 定义函数结构:

typescript 复制代码
// 定义 PersonInterface接口
interface PersonInterface {
    (name:string, age: number) : void
}

let student:PersonInterface = (name:string, age: number) => {
    console.log(`我的名字是${name},我今年${age}岁。`)
}
student('小红', 15) // 我的名字是小红,我今年15岁。

4、接口之间的继承:

接口继承后,最终定义的数据需要满足所有接口的结构

typescript 复制代码
// 定义PersonInterface接口
interface PersonInterface {
    name:string;
    age: number;
    introduce: (grade:string) => void
}
// HobbyInterface接口 继承 PersonInterface接口
interface HobbyInterface extends PersonInterface{
    hobby: string;
    gender?:string;
}
// 需要满足两个接口的内容
let student: HobbyInterface = {
    name: '小红',
    age: 8,
    hobby: '读书',
    introduce: (grade:string) => {
        console.log(`我的名字是${student.name},我今年${student.age}岁,上${grade}年级,喜欢${student.hobby}。`)
    }
}
student.introduce('二') // 我的名字是小红,我今年8岁,上二年级,喜欢读书。

5、接口自动合并:

等同于type的交叉类型,但是比交叉类型灵活

可以为同一个接口,追加不同的结构

typescript 复制代码
// 定义PersonInterface接口
interface PersonInterface {
    name:string;
    age: number;
    introduce: (grade:string) => void
}
// 给 PersonInterface接口 新增结构
interface PersonInterface{
    hobby: string;
    gender?:string;
}
let student: PersonInterface = {
    name: '小红',
    age: 8,
    hobby: '读书',
    introduce: (grade:string) => {
        console.log(`我的名字是${student.name},我今年${student.age}岁,上${grade}年级,喜欢${student.hobby}。`)
    }
}
student.introduce('二') // 我的名字是小红,我今年8岁,上二年级,喜欢读书。

6、使用场景:

① 定义对象的格式:

数据模型、API响应格式、配置对象

② 类的契约:

规定一个类需要实现的属性和方法

③ 自动合并:

扩展第三方库的类型

八、泛型:

1、定义:

在定义函数、类、接口的时候,使用类型参数表示 未指定的类型,需要等到使用的时候才会 被指定具体的类型

2、优点:

可以让同一段代码适用多个类型,同时保持类型的安全性。

3、使用:

① 泛型函数:

定义函数:入参要使用泛型 <T> ,那么函数必须要指定 <T>

使用函数:函数要指定入参的类型

typescript 复制代码
// 泛型的字母T可以随意更改
function func<T> (a:T) {
    console.log(a)
}
func<number>(111)
func<string>('哈哈')

② 指定多个泛型:

typescript 复制代码
function func<T,H> (a:T, b:H, c:number) : T | H {
    return c % 2 ? a : b
}
type Box = {
    width: number;
    height: number;
}
let res = func<number, Box>(123, {
    width: 1,
    height: 3
}, 3)
console.log(res) // 123

③ 泛型接口:

定义:属性要使用泛型 <T> ,那么接口必须要指定 <T>

使用:接口要指定入参的类型

typescript 复制代码
interface PersonInterface<T> {
    name: string;
    age: number;
    ExtroInfo: T;
}

let p1:PersonInterface<number> = {
    name: '小红',
    age: 18,
    ExtroInfo: 20
}

let p2:PersonInterface<string> = {
    name: '小红',
    age: 18,
    ExtroInfo: '二年级'
}

④ 泛型约束:

使用:在指定泛型的时候,也要对泛型本身进行约束

typescript 复制代码
interface PersonInterface {
    name: string;
    age: number;
}

function func<T extends PersonInterface>(a: T): void {
    console.log(`我叫${a.name},今年${a.age}岁`)
}

func({name: '小红', age: 18}) // 我叫小红,今年18岁

⑤ 泛型类:

typescript 复制代码
class Person<T> {
    constructor(
        public name:string,
        public age: number,
        public info: T
    ){}
    say() {
        console.log(`我的名字是${this.name}, 年龄是${this.age}。`)
        console.log(this.info)
    }
}

let p1 = new Person<string>('小红', 18, '哈哈哈')
p1.say() // 我的名字是小红, 年龄是18。   哈哈哈
let p2 = new Person<number>('小兰', 8, 666)
p2.say() // 我的名字是小兰, 年龄是8。   666

type Student = {
    gender: string;
    title: string
}
let p3 = new Person<Student>('小李', 28, {
    gender: '男',
    title: '自我介绍'
})
p3.say() // 我的名字是小李, 年龄是28。  {gender: '男', title: '自我介绍'}

九、类型声明文件:xx.d.ts

ts中使用js文件时,一般会有 xx.d.ts 文件来 为js文件提供类型信息,使得ts可以对js文件进行类型检查和提示。

typescript 复制代码
// xx.d.ts 文件内容示例:
declare const count: number;
// ...

十、装饰器:

文章链接:https://blog.csdn.net/Y1914960928/article/details/146209433

相关推荐
returnShitBoy39 分钟前
前端面试:如何实现预览 PDF 文件?
前端·pdf
烂蜻蜓2 小时前
HTML 表格的详细介绍与应用
开发语言·前端·css·html·html5
圣京都4 小时前
react和vue 基础使用对比
javascript·vue.js·react.js
returnShitBoy4 小时前
前端面试:axios 是否可以取消请求?
前端
u0103754564 小时前
fiddler+雷电模拟器(安卓9)+https配置
前端·测试工具·fiddler
海上彼尚4 小时前
Vue3中全局使用Sass变量方法
前端·css·sass
鱼樱前端4 小时前
Vue3+TS 视频播放器组件封装(Video.js + Hls.js 最佳组合)
前端·javascript·vue.js
烛阴5 小时前
JavaScript 函数进阶之:Rest 参数与 Spread 语法(二)
前端·javascript
GISer_Jing5 小时前
ES6回顾:闭包->(优点:实现工厂函数、记忆化和异步实现)、(应用场景:Promise的then与catch的回调、async/await、柯里化函数)
前端·ecmascript·es6