常用类型和语法
1.any
任意类型,⼀旦将变量类型限制为any
,意味着放弃了对该变量的类型检查。
let a:any
//以下赋值无警告
a=100
a='你好'
a=false//隐式any
let b
注意:any
类型的变量,可以赋值给任意类型的变量
let c:any
c=9let x:string
x=c //没有报错
2.unknown
未知类型,适用于:起初不确定数据的具体类型,要后期才能确定
unknown
可以理解为一个类型安全的any
let a:unknown
//不会报错
a=100
a=false
a='你好'//设置x的数据类型为string
let x:string
x=a//不能将类型'unknown"分配给类型”string“
unknown
会强制开发者在使用之前进行类型检查
//第一种
if(typeof a==='string'){x=a
}//第二种: 加断言
x=a as string//第三种
x=<string>a
- 读取
any
类型数据的任何属性都不会报错,unknown
相反
let str1:string
str1.toUpperCase()//不报错let str2:any
str2.toUpperCase()//不报错let str3:unknown
str3.toUpperCase()//报错
//强制断言
(str3 as string).toUpperCase()
3.never
never
的含义是:任何值都不是,即:不能有值,例如undefined
、nul
0
都不行!
never
限制函数返回值的
function demo():never{
}
注意:ts
中没有指明任何返回值的函数调用结束也会返回undefined
function throwError(str:string):never{//程序抛出异常,并立刻结束对函数的调用throw new Error('程序异常退出'+str)
}
4.void
void
的含义是空,即:函数不返回任何值,调用者也不应依赖其返回值进行任何操作!限制函数返回值的,不能对变量进行限制。
void
通常用于函数返回值声明
function logMessage(msg:string):void{console.log(msg)
}
logMessage('你好')
注意:没有编写return
指定函数返回值,所以logMessage
函数是没有显式
返回值的,但会有一个隐式返回值,是undefined
,虽然函数返回类型为void
,但也是可以接受undefined
的,简单记:undefined
是void
可以接受的一种“空”。
- 以下写法符合规范
function logMessage(msg:string):void{console.log(msg)
}function logMessage(msg:string):void{console.log(msg)return;
}function logMessage(msg:string):void{console.log(msg)return undefined
}
- 那限制函数返回值时,是不是
undefined
和void
就没区别呢?—有区别。因为返回值类型为void的函数,调用者不应依赖其返回值进行任何操作!对比下面两段代码:
function logMessage(msg:string):void{console.log(msg)
}let result = logMessage('你好')if(result){//此行报错:无法测试"void"类型的表达式的真实性console. log('logMessage有返回值')
}
function logMessage(msg:string):undefined{console.log(msg)
}let result = logMessage('你好')if(result){//此行无警告console. log('logMessage有返回值')
}
5.object
实际开发中用的相对较少,因为范围太大了。
object:所有非原始类型,可存储:对象、函数、数组等。
Object:所有可以调用Object
方法的类型。除了undefined
和null
的任何值。
声明对象类型
- 限制一般对象可以使用下面形式
//限制person1对象必须有name属性,age为可选属性,可有可无
let personl: { name: string, age?: number }//含义同上,也能用分号做分隔
let person2: { name: string; age?: number }//含义同上,也能用换行做分隔
let person3:{name: stringage?:number
}
//如下赋值均可以
person1={name:'李四',age:18}
person2={name:'张三'}
person3={name:'王五'}
- 索引签名:允许定义对象可以具有任意数量的属性,这些属性的键和类型是可变的,常用于:描述类型不确定的属性,(具有动态属性的对象)。
//限制person对象必须有name属性,可选age属性但值必须是数字,同时可以有任意数量、任意类型的其他属性
let person: {name: stringage?:number[key:string]:any//索引签名,完全可以不用key这个单词,换成其他的也可以,保证key的类型是string,其值是any类型//赋值合法
person = {name:'张三',age:18,gender:'男',city:'成都'
}
声明函数类型
//对函数接受的参数进行限制,对函数返回值进行限制
let count: (a: number, b: number) => numbercount = function (x, y) {return x + y
}//箭头函数,js中的写法
count=(x,y)=>{return x+y
}
备注:
TypeScript
中的=>
在函数类型声明时表示函数类型,描述其参数类型和返回类型。
JavaScript
中的=>
是一种定义函数的语法,是具体的函数实现。
函数类型声明还可以使用:接口、自定义类型等方式。
声明数组类型
let arr1:string[]
let arr2:Array<string>//泛型arr1=['a','b','c']
arr2=['hello','world']
6.tuple
元组(Tuple)是一种特殊的数组类型,可以存储固定数量的元素,并且每个元素的类型是已知的且可以不同。元组用于精确描述一组值的类型,?表示可选元素。
let arr1:[string,number]
arr1=['hello',1]let arr2:[string,boolean?]
arr2=['hello']let arr3:[number,...string[]]//任意多个string类型
arr3=[100,'a','b']
7.enum
枚举(enum)
可以定义一组命名常量,它能增强代码的可读性,也让代码更好维护。
- 数字枚举一种最常见的枚举类型,其成员的值会自动递增,且数字枚举还具备反向映射的特点,在下面代码的打印中,不难发现:可以通过值来获取对应的枚举成员名称。
//定义一个描述【上下左右】方向的枚举Direction
enum Direction {Up,Down,Left,Right
}console.log(Direction)//打印Direction会看到如下内容
/*O:'Up',1:'Down',2:'Left',3:'Right',Up:0,Down:1,Left:2,Right:3
*///反向映射
console.log(Direction.Up)
console.log(Direction[0])//此行代码报错,枚举中的属性是只读的
Direction.Up = 'shang'
指定枚举成员的初始值,后面成员从初始值自增
enum Direction {Up = 6,Down,Left,Right
}console.log(Direction.Up); //输出:6
console.log(Direction.Down); // 输出:7
- 字符串枚举
enum Direction {Up="up",Down = "down",Left = "left",Right = "right"
}let dir: Direction = Direction.Up;
console.log(dir);// 输出:"up"
- 常量枚举
官方描述:常量枚举是一种特殊枚举类型,它使用const
关键字定义,在编译时会被内联,避免生成一些额外的代码。
编译时内联:所谓“内联”其实就是TypeScript
在编译时,会将枚举成员引用替换为它们的实际值,而不是生成额外的枚举对象。这可以减少生成的JavaScript
代码量,并提高运行时性能。
普通枚举:
enum Directions {Up,Down,Left,Right
}let x = Directions.Up;
编译生成js
"use strict";
var Directions;
(function (Directions) {Directions[Directions["Up"] = 0O] = "Up";Directions[Directions["Down"] = 1] = "Down";Directions[Directions["Left"] = 2] = "Left";Directions[Directions["Right"] = 3] = "Right";
})(Directions |I (Directions = {}));let x = Directions.Up;
常量枚举
enum Directions {Up,Down,Left,Right
}let x = Directions.Up;
编译生成js
"use strict";let x = 0 /* Directions.Up */;
8.type
type
可以为任意类型创建别名,让代码更简洁、可读性更强,同时能更方便地进行类型复用和扩展。
- 基本用法
type num=numberlet price:num
price=100
- 联合类型
type Status=number|stringfunction printStatus(data:Status):void{console.log(data)
}printStatus(404)
printStatus('404')type Gender='男'|'女‘
function logGender(str:Gender){console.log(str)
}
logGender('男')
logGender('女')
- 交叉类型
//面积
type Area = {height:number;//高width:number;//宽
};//地址
type Address = {num:number;//楼号cell:number; //单元号room:string;//房间号
}//定义类型House,且House是Area和Address组成的交叉类型
type House = Area & Address;//包含Area和Address的所有属性
const house: House = {height:180,width:75,num:6,cell: 3,room:'702'
};
9.特殊情况
- 正常情况
在函数定义时,限制函数返回值为void,那么函数的返回值就必须是空。
function demo():void{//返回undefined合法return undefined//以下返回均不合法return 100return falsereturn nullreturn []
}
demo()
- 特殊情况
//自定义一个LogFunc类型,它的类型是函数,返回值为void
type LogFunc = () => void//报错,此时LogFunc为类型,类似于number=9
LogFunc=function(){
}const f1: LogFunc = () => {return 100;//允许返回非空值
}const f2:LogFunc= ()=>200; // 允许返回非空值const f3: LogFunc = function(){return 300;//允许返回非空值
}
10.类相关知识
class Person {//属性声明name: stringage: number//构造器,必须写类型constructor(name: string, age: number){this.name = namethis.age = age}//方法speak() {console.log('我叫:${this.name},今年${this.age}岁')}
}//Person实例
const p1=new Person('周杰伦',38)//继承
class Student extends Person{grade:stringconstructor(name:string,age:number,grade:string){super(name,age)this.grade=grade}study(){console.log('${this.name}正在学习中。。。。。')}//重写父类方法 1.与父类相同的名字 2.在函数前加overrideoverride speak(){console.log('我是学生,我叫:${this.name},今年${this.age}岁')}
}const s1=new Student('许同学',16,'一年级')
s1.study()
11.属性修饰符
属性简写形式
简写前
class Person {public name: string;public age: number;constructor(name: string, age: number){this.name = name;this.age = age;}
}
简写后
class Person {constructor(public name: string,public age: number){}
}
12.抽象类
抽象类是一种无法被实例化的类,专门用来定义类的结构和行为,类中可以写抽象方法,也可以写具体实现。抽象类主要用来为其派生类提供一个基础结构,要求其派生类必须实现其中所有的抽象方法。抽象类不能实例化,其意义是可以被继承,抽象类里可以有普通方法、也可以有抽象方法。
abstract class Package {constructor(public weight: number){}//抽象方法:用来计算运费,不同类型包裹有不同的计算方式abstract calculate(): number//abstract calculate(x:string,y:string): number//具体方法:打印包裹详情printPackage(){console.log('包裹重量为:${this.weight}kg,运费为:${this.calculate()}元');}
}//标准包裹
class StandardPackage extends Package{constructor(weight:number,public unitPrice:number //每公斤的固定费率,简写形式){super(weight)}//实现抽象方法:计算运费calculate(): number {return this.weight * this.unitPrice;}
}//创建标准包裹实例
const s1 = new StandardPackage(10,5)
s1.printPackage()//特快包裹
class ExpressPackage extends Package {constructor(weight:number,private unitPrice:number,//每公斤的固定费率(快速包裹更高)private additional:number // 超出1okg以后的附加费){ super(weight)}//实现抽象方法:计算运费calculate(): number {if(this.weight > 10){//超出10kg的部分,每公斤多收additional对应的价格return 10 * this.unitPrice + (this.weight - 10) * this.additional}else {return this.weight * this.unitPrice;}}}//创建特快包裹实例
const e1 = new ExpressPackage(13,8,2)
el.printPackage()
总结:何时使用抽象类?
- 定义通用接口:为一组相关的类定义通用的行为(方法或属性)时。
- 提供基础实现:在抽象类中提供某些方法或为其提供基础实现,这样派生类就可以继承这些实现。
- 确保关键实现:强制派生类实现一些关键行为。
- 共享代码和逻辑:当多个类需要共享部分代码时,抽象类可以避免代码重复。
13.interface(接口)
interface
是一种定义结构的方式,主要作用是为:类、对象、函数等规定一种契约,这样可以确保代码的一致性和类型安全,但要注意interface只能定义格式,不能包含任何实现!
- 定义类结构
//定义接口
interface PersonInterface {name: stringage:numberspeak(n: number): void
}//定义一个类 Person,实现PersonInterface接口
class Person implements PersonInterface {constructor(public name: string,public age: number){}//实现接口中的speak方法speak(n: number): void {for (let i = 0; i< n; i++) {//打印出包含名字和年龄的问候语句console.log('你好,我叫${this.name},我的年龄是${this.age}');}}
}//创建一个Person类的实例p1,传入名字'tom'和年龄18
const p1 = new Person('tom', 18);
pl.speak(3)
- 定义对象结构
interface UserInterface {name:stringreadonly gender:string //只读属性age?:number// 可选属性run: (n: number) => void
}const user: UserInterface = {name:"张三",gender:'男',age:18,run(n) {console.log('奔跑了${n}米')
}
- 定义函数结构
interface CountInterface {(a: number,b: number):number;
}
const count: CountInterface = (x, y) => {return x + y
}
- 接口之间的继承
interface PersonInterface {name:string //姓名age:number // 年龄
}//继承的接口
interface StudentInterface extends PersonInterface {grade:string //年级
}const stu: StudentInterface = {name:'张三',age:25,grade:'高三'
}
- 接口的自动合并
// PersonInterface接口
interface PersonInterface {//属性声明name: stringage:number
}//给PersonInterface接口添加新属性
interface PersonInterface {//方法声明speak(): void
}// Person类实现PersonInterface
class Person implements PersonInterface {name:stringage:number//构造器constructor(name: string, age: number) {this.name = namethis.age = age}//方法speak() {console.log('你好!我是老师:',this.name)}
}
总结:何时使用接口?
- 定义对象的格式:描述数据模型、AP响应格式、配置对象....等等,是开发中用的最至的场景。
- 类的契约:规定一个类需要实现哪些属性和方法。
- 扩展已有接口:一般用于扩展第三方库的类型,这种特性在大型项目中可能会用到。
一些相似的概念的区别
1.interface与type
相同点
interface和type都可以用于定义对象结构,在定义对象结构时两者可以互换。
//使用 interface 定义 Person 对象
interface PersonInterface {name:string;age:number;speak(): void;
}// 使用type 定义 Person 对象
type PersonType ={name: string;age:number;speak(): void;
}
// 使用PersonInterface
let person: PersonInterface ={name:'张三’,age:18,speak(){console.log('我叫:${this.name},年龄:${this.age}')}
}//使用PersonType
let person: PersonType = {name:'张三',age:18,speak(){console.log('我叫:${this.name},年龄:${this.age}')}
}
不同点
interface:更专注于定义对象和类的结构,支持继承、合并。
type:可以定义类型别名、联合类型、交叉类型,但不支持继承和自动合并。
接口的继承与合并
interface PersonInterface {name:string //姓名age:number//年龄
}//利用接口的自动合并
interface PersonInterface {speak:()=>void
}
//利用接口之间的继承
interface StudentInterface extends PersonInterface {grade:string //年级
}const student: StudentInterface = {name:'李四',age:18,grade:'高二'speak() {console.log(this.name,this.age,this.grade)}
}
type的交叉类型
//使用type定义Person类型,并通过交叉类型实现属性的合并
type PersonType = {name:string;//姓名age:number;//年龄
}&{speak: () => void;
};//使用type定义Student类型,并通过交叉类型继承PersonType
type StudentType = PersonType &{grade:string;//年级
}const student: StudentType = {name:'李四',age:18,grade:'高二',speak() {console.log(this.name, this.age, this.grade);
};
2.interface与抽象类
相同点:都能定义一个类的格式(定义类应遵循的契约)
不相同:接口:只能描述结构,不能有任何实现代码,一个类可以实现多个接口。
抽象类:既可以包含抽象方法,也可以包含具体方法,一个类只能继承一个抽象类。
泛型
泛型允许我们在定义函数、类或接口时,使用类型参数来表示未指定的类型,这些参数在具体
使用时,才被指定具体的类型,泛型能让同一段代码适用于多种类型,同时仍然保持类型的安
全性。
如下代码中
- 泛型函数
function logData<T>(data: T): T{console.log(data)return data
}logData<number>(100)
logData<string>('hello')
- 多个泛型
function logData<T, U>(datal: T, data2: U): T | U{console.log(datal,data2)return Date.now()%2? datal : data2
}logData<number, string>(100, 'hello')
logData<string, boolean>('ok', false)
- 泛型接口
interface PersonInterface<T> {name:string,age:number,extraInfo: T//额外信息
}let pl: PersonInterface<string>
let p2: PersonInterface<number>pl={name:'张三',age:18,extraInfo:'一个好人'}
p2={ name:'李四',age:18,extraInfo:250}
- 泛型类
class Person<T> {constructor(public name: string,public age: number,public extraInfo:T){}speak(){console.log('我叫${this.name}今年${this.age}岁了')console.log(this.extraInfo)}
}
const p1 = new Person<number>("tom", 30, 250);
类型声明文件
类型声明文件是TypeScript中的一种特殊文件,通常以·d.ts作为扩展名。它的主要作用是为现有的JavaScript代码提供类型信息,使得TypeScript能够在使用这些JavaScript库或模块时进行类型检查和提示。
export function add(a, b) {return a + b;
}export function mul(a, b) {return a * b;
}
declare function add(a: number, b: number): number;
declare function mul(a: number, b: number): number;
export { add, mul };
// example.ts
import { add, mul } from "./demo.js";
const x = add(2, 3); // x 类型为 number
consty = mul(4, 5); // y 类型为 number
console.log(x,y)