TS学习-2
常用类型和语法
1.any
任意类型,⼀旦将变量类型限制为any,意味着放弃了对该变量的类型检查。
let a:any
//以下赋值无警告
a=100
a='你好'
a=false
//隐式any
let b
注意:any类型的变量,可以赋值给任意类型的变量
let c:any
c=9
let 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: string
age?:number
}
//如下赋值均可以
person1={name:'李四',age:18}
person2={name:'张三'}
person3={name:'王五'}
- 索引签名:允许定义对象可以具有任意数量的属性,这些属性的键和类型是可变的,常用于:描述类型不确定的属性,(具有动态属性的对象)。
//限制person对象必须有name属性,可选age属性但值必须是数字,同时可以有任意数量、任意类型的其他属性
let person: {
name: string
age?:number
[key:string]:any//索引签名,完全可以不用key这个单词,换成其他的也可以,保证key的类型是string,其值是any类型
//赋值合法
person = {
name:'张三',
age:18,
gender:'男',
city:'成都'
}
声明函数类型
//对函数接受的参数进行限制,对函数返回值进行限制
let count: (a: number, b: number) => number
count = 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=number
let price:num
price=100
- 联合类型
type Status=number|string
function 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 100
return false
return null
return []
}
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: string
age: number
//构造器,必须写类型
constructor(name: string, age: number){
this.name = name
this.age = age
}
//方法
speak() {
console.log('我叫:${this.name},今年${this.age}岁')
}
}
//Person实例
const p1=new Person('周杰伦',38)
//继承
class Student extends Person{
grade:string
constructor(name:string,age:number,grade:string){
super(name,age)
this.grade=grade
}
study(){
console.log('${this.name}正在学习中。。。。。')
}
//重写父类方法 1.与父类相同的名字 2.在函数前加override
override 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: string
age:number
speak(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:string
readonly 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: string
age:number
}
//给PersonInterface接口添加新属性
interface PersonInterface {
//方法声明
speak(): void
}
// Person类实现PersonInterface
class Person implements PersonInterface {
name:string
age:number
//构造器
constructor(name: string, age: number) {
this.name = name
this.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)

浙公网安备 33010602011771号