TypeScript 学习之路 - 基础篇
一、介绍
- 一种由微软开发的自由和开源的编程语言,简称 TS。
- TypeScript 是 JavaScript 的超集,即包含 JavaScript 的所有元素,能运行 JavaScript 的代码,并扩展了 JavaScript 的语法。
- 相比于 JavaScript,它还增加了 静态类型、类、模块、接口 和 类型注解 方面的功能,相对于 JavaScript,TypeScript 属于 强类型 语言,所以对于项目而言,会使代码更加规范,从而解决了大型项目代码的复杂性。 更易于大项目的开发
- TS 是不能被浏览器直接识别的,所以在编译的时候,TS 文件会先编译为 JS文件。
TypeScript 与 ES5、ES6+ 之间的关系
TypeScript 与 JavaScript 的区别
TypeScript | JavaScript |
---|---|
JavaScript的超集,用于解决大型项目的复杂性 | 一种脚本语言,用于创建动态网页 |
能在编译期间发现错误并纠正错误 | 只能在运行时发现错误 |
是强类型语言,支持静态类型和动态类型 | 是弱类型语言,没有静态类型 |
最终是被编译成JavaScript代码,使得浏览器可以理解 | 直接在浏览器使用 |
支持模块、泛型和接口 | 不支持模块、泛型和接口 |
二、安装编译环境
安装并查看版本
// 安装
npm install -g typescript
// 或者
yarn global add typescript
// 查看版本
tsc -v
创建配置文件
// tsc --init
{
"compilerOptions": {
/* Language and Environment */
"target": "es6",
/* Modules */
"rootDir": "./src",
/* Emit */
"outDir": "./dist",
/* Type Checking */
"strict": true,
}
}
创建一个ts文件
function greeter(person) {
return "Hello, " + person;
}
let user = "Jane User";
document.body.innerHTML = greeter(user);
编译
tsc greeter.ts // greeter.ts => greeter.js
三、数据类型
类型 | 例子 | 描述 |
---|---|---|
number | 1 50 1.5 | 任意数字 |
string | ‘hello world’ ‘你好’ | 任意字符串 |
boolean | true false | 布尔值true或false |
字面量 | 其本身 | 限制变量的值就是该字面量的值 |
any | * | 任意类型 |
unknown | * | 类型安全的any |
void | 空值(undefined) | 没有值或undefined |
never | 没有值 | 不能是任何值 |
object | {id:1, name:“pengyuyan”} | 任意的JS对象 |
array | [1,2,3] | 任意js数组 |
tuple | [4,5] | 元素,TS新增类型,固定长度数组 |
enum | enum(A,B) | 枚举,TS中新增类型 |
基础类型
// number string boolean
var a: number = 1; // 添加类型注释来显式指定变量的类型
var a2 = 2; // // 不需要类型定义--'a2'推断为类型 'number'
let b: string = 'hello';
//字符串模板
let b1:string=`${b}` // "hello"
const c: boolean = true;
// symbol
// bigint
let f: symbol = Symbol(); // Symbol是es6/es2015才出现的类型
let f1:symbol = Symbol(); // Symbol是es6/es2015才出现的类型
console.log(f === f1) // false
let g: bigint = 10n // bigint是es2020新增语法
null 和 undefined
默认情况下它们是所有类型的子类型,即可以赋值给任意类型
let a: string = 'hello'
a = null //right
a = undefined // right
// 这两个类型只有 自己 null 和 undefined 两个类型一旦赋值上,就不能在赋值给任何其他类型
let d: null = null;
let e: undefined = undefined;
let d: string = 'pengyuyan' // error
当我们在 tsconfig.js 文件中设置 strictNullChecks 为 true 时,就不能将 null 和 undefined 赋值给除 它们自身 和 void 之外的任意类型了。
// "strictNullChecks": true
let b: string = 'hello'
b = null // error,不能将类型“null”分配给类型'string'。
let c: string | null = 'hi'
c = null // right
c = undefined // error,不能将类型'undefined'分配给类型'string | null'。
可选参数 (“strictNullChecks”: true)
// "strictNullChecks": true
function f(x: number, y?: number){
return x + (y || 0)
}
f(1, 2) // 3
f(1) // 1
f(1, undefined) // 1
f(1, null) // error,类型“null”的参数不能赋给类型“number | undefined”的参数。
可选属性 (“strictNullChecks”: true)
// "strictNullChecks": true
interface PositionInterface{
x: number
y?: number
}
let p: PositionInterface = {x: 10}
p.y = 'abc' // error,不能将类型“"abc"”分配给类型“number | undefined”。
p.y = null // error,不能将类型“null”分配给类型“number | undefined”。
p.y = undefined // right
引用类型
array
TS 中要求数组中的每一项必须是 同一个数据类型。
语法:
变量名: 数组中数据的类型[ ] = 数组值
变量名: Array<数组中数据的类型> = 数组值
const arr1: number[] = [1, 2, 3];
const arr2: Array<number> = [1, 2, 3];
const arr3: Array<string> = ['1', '2']
const arr4: Array<number> = [1, 2, '3'] // error 必须是同一个数据类型
//如果想要是数字类型或字符串类型,需要使用 |
const arr5: Array<number | string> = [1, 2, '3']
tuple(元祖)
- 元组中,允许一个数组中保存 多个类型 的数据。
- *注意:可以把 元祖 一个任意类型并且 长度有限 的数组
const arr5: [string, number] = ['1', 2];
object
// 方式一:
let obj:object;
// 或
let obj:{};
// object 表示一个js对象
let a :object;
a = {
name:"pengyuyan",
age:18
};
// 方式二
let b:{};
b = {
name:"pengyuyan",
age:18
}
// 定义对象时,需要定义出对象中有哪些属性,每一个属性的值是什么类型,指定的属性的个数,多一个属性也不行,少一个属性也不行
const obj: { age: number, name: string } = { age: 18, name: 'pengyuyan' };
// 如果想指定属性是可选的(可有可无)
const obj:{
name:string,
age?:number
}
obj = {
name:"pengyuyan"
}
function
声明的函数在调用的时候,参数个数要和声明时候的参数个数保持一致
//没有返回值的函数可以用void声明
// 参数类型定义
const f1 = (name:string,id:number): void => {
console.log("我是没有返回值的箭头函数");
};
function f2(name:string,id:number):void{
console.log("我是没有返回值的普通函数");
}
//有返回值的箭头函数声明是这样的
// 返回类型注释
const f3 = (): string => {
return "pengyuyan=>"
};
//有返回值的普通函数声明是这样的
function f4():string{
return "pengyuyan"
}
//函数表达式的双向限定
//上述f1其实只对=右侧做了限制,对左侧并没有
//完善一点,可以这样 => 用来表示函数的定义,左输入类型,需要用括号括起来,右输出类型
const f5:(name:string,id:number)=>void = (name:string,id:number): void => {
console.log("我是没有返回值的箭头函数");
};
// 函数的可选参数
// 注意可选参数要在确定参数后
function f6(name:string,id?:number):string{
return "pengyuyan"
}
//函数参数默认值
function f7(name:string,id:number=1):string{
return `${name}--${age}`
}
//此时可选参数不必一定在确定参数后,但是调用有问题
function f8(name:string,sex?:string,age:number=1):string{
return `${name}--${age}--${sex}`
}
console.log(f8('pengyuyan','male',18)) // "pengyuyan--18--male"
//剩余参数
function f9(...arr:number[]):number[]{
return arr
}
console.log(f9(1,2,3,4,5)) // [1,2,3,4,5]
特殊类型
any
可以访问它的任何属性,可以将它分配给赋予任意类型的值
let num: any = 666
let anyVal: any
anyVal = 888 // number
anyVal = 'hello' // string
let a;(隐式any) //声明变量不赋值,就是any 等效于let a:any = 10;(显示any)
a = 1
a = 'helloWorld'
let b = string //给 b 变量设置类型string
b = a; //a 是 any 类型,它可以赋值给任意变量 此时 b 的类型也被影响了
unknown
未知的类型,TS中所有基础类型的父类型,所有基础类型都能赋值为 unknown类型
let a:unknown;
a = 10;
a = "helloWorld";
console.log(a) // helloWorld
a.age // error
let b : string = "pengyuyan" // b 类型: string
console.log(b = a); // error a 是 unknown, b 的类型不会被影响
unknown类型直接赋值给其他变量的方法
typeof 进行类型判断
let num1: unknown = 666;
if(typeof num1 === "number") {
let num2 = num1
}
类型断言
let num1: unknown = 666;
let num2 = num1 as number);
// 或
let num3 = <number>num1;
any 和 unkown 的区别
unknown类型会更加严格:在对 unknown类型的值执行大多数操作之前,不能直接赋值给其他变量,必须将这个 unknown类型的变量断言为具体的类型,才可以继续使用。
而在对 any类型的值执行操作之前,会绕过类型检查,直接可用。
let foo: any = 123;
console.log(foo.msg); // 符合TS的语法
let value1: unknown = foo; // OK
let value2: any = foo; // OK
let value3: string = foo; // OK
let bar: unknown = 456; // OK
console.log(bar.msg); // Error
let value4: unknown = bar; // OK
let value5: any = bar; // OK
let value6: string = bar; // Error
因为bar是一个未知类型(任何类型的数据都可以赋给 unknown 类型),所以不能确定是否有msg属性。不能通过TS语法检测;而 unkown 类型的值也不能将值赋给 any 和 unkown 之外的类型变量
总结: any 和 unknown 都是顶级类型,但是 unknown 更加严格,不像 any 那样不做类型检查,反而 unknown 因为未知性质,不允许访问属性,不允许赋值给其他有明确类型的变量
never 类型
在 TS中使用 never类型来表示不应该存在的值的类型,例如:
- 一个抛出异常的函数
- 一个永远不会返回的函数的返回值类型
注意:never 类型是任何类型的子类型,可以赋值给任意类型*。但是没有类型是 never类型的子类型,即使是 any类型也不能赋值给 never类型
const a:never;
a = 123 // error
a = (()=>{
throw new Error('错误')
})
const fn: ()=>never = () => {
throw new Error('error')
}
const [n,setN] = React.useState<number>(1)
const add: ()=>never = () => {
while(true){ setN(i=>i+1) }
}
Enum(枚举)
可以定义带名字的 常量
- 枚举的类型 只能 是 string 或 number
- 定义的名称不能为 关键字
按枚举成员分类
数字枚举
*注意:
- 是 数字类型
- 如果有 默认值,会影响到后面的值
- 支持 反向映射
enum Direction {
Up = 1, // 1
Down, // 2
Left, // 3
Right // 4
}
enum Direction {
Up, // 0
Down = 3, // 3
Left, // 4
Right // 5
}
// 如上,我们定义了一个数字枚举, Up使用初始化为 1。 其余的成员会从 1开始自动增长。
enum Direction {
Up, // 0
Down, // 1
Left, // 2
Right // 3
}
// 当我们不在乎成员的值的时候,这种自增长的行为是很有用处的,但是要注意每个枚举成员的值都是不同的。
enum Days {Sun = 3, Mon = 1, Tue, Wed, Thu, Fri, Sat};
const date: { id: number, name: string, days: Days } = { id: 1, name: '周三', days: Days.Wed }
console.log(Days["Sun"] === 3); // true
console.log(Days["Wed"] === 3); // true
console.log(Days[3] === "Sun"); // false
console.log(Days[3] === "Wed"); // true
// 如果未手动赋值的枚举项与手动赋值的重复了,TypeScript 是不会察觉到这一点的,但建议尽量避免
字符串枚举
在一个字符串枚举里,每个成员都必须用字符串字面量,或另外一个字符串枚举成员进行初始化
*注意:
- 必须要有 默认值
- 不 支持 反向映射
enum Direction {
Up = "UP",
Down = "DOWN",
Left = "LEFT",
Right = "RIGHT",
}
const a: Direction = Direction.Up //a = UP
const b: Direction = Direction.Down //b = DOWN
// 由于字符串枚举没有自增长的行为,字符串枚举可以很好的序列化。
// 字符串枚举允许你提供一个运行时有意义的并且可读的值,独立于枚举成员的名字。
异构枚举 (不建议)
将 数字 枚举与 字符串 枚举 混用,不建议
反向映射
除了创建一个以 属性名 做为对象成员的对象之外,数字枚举成员(字符串枚举成员没有反向映射)还具有了 反向映射,从枚举值到枚举名字
enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat};
console.log(Days["Sun"] === 0); // true
console.log(Days["Mon"] === 1); // true
console.log(Days["Tue"] === 2); // true
console.log(Days["Sat"] === 6); // true
console.log(Days[0] === "Sun"); // true
console.log(Days[1] === "Mon"); // true
console.log(Days[2] === "Tue"); // true
console.log(Days[6] === "Sat"); // true
按声明方式
普通枚举
enum Color {Red, Green, Blue = "blue".length};
// 值由计算所得变为计算所得项 如,"blue".length 就是一个计算所得项
enum Color {Red = "red".length, Green, Blue};
// 上述代码会报错,因为Red是计算项,而Green紧接其后却无法获取初始值.
// 根据官方定义,不带初始化器的枚举要么被放在第一的位置,要么被放在使用了数字常量或其它常量初始化了的枚举后面。
常量枚举 (通过const enum 定义的枚举)
const enum Directions {
Up,
Down,
Left,
Right
}
let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];
// var directions = [0 /* Up */, 1 /* Down */, 2 /* Left */, 3 /* Right */];
外部枚举
用来描述 已经存在的枚举类型,当前环境中 已经存在的对象 的,这个对象可以存在 任意 的地方,但是一定是 已声明 的。
*注意:不支持 反向映射
参考:https://stackoverflow.com/questions/28818849/how-do-the-different-enum-variants-work-in-typescript
declare enum Enum {
A = 1,
B,
C = 2
}
使用枚举
- 通过 枚举的属性 来访问 枚举成员
- 通过 枚举的名字 来访问 枚举类型
enum Response {
No = 0,
Yes = 1,
}
function respond(recipient: string, message: Response): void {
// ...
}
respond("Princess Caroline", Response.Yes)
void
- 可以用来声明变量,但只能作用在 undefined 身上,null 也不行。只有 tsconfig.json 中strictNullChecks 属性设置为 false 时,null 才能赋值给 void。
- 一般用于当一个函数 没有返回值时,TS 会默认他的返回值是 void 类型
const a = ():void => {} // 等价于 const a = () => {}
const b = ():void => { return 1 } // error
const c = ():void => { return '2' } // error
const d = ():void => { return true } // error
const e = ():void => { return } // ok
const f = ():void => { return undefined } //ok
const g:void = 1 // error
const h:void = undefined // error
其他类型
联合类型 |
由两个或多个其他类型组成的类型,表示可能是这些类 型中的任何一种的值。我们将这些类型中的每一种称为联合类型的成员。
function printId(id: number | string) {
console.log("Your ID is: " + id);
}
printId(101); // ok
printId("202"); // ok
printId({ myID: 22342 }); // error
在联合类型中,unknown 类型会吸收 任何 类型。联合类型中有 unknown,那么最终得到的都是 unknown 类型
type a = unknown | null; // unknown
type b = unknown | undefined; // unknown
type c = unknown | string; // unknown
type d = unknown | number[]; // unknown
// 如果至少一种组成类型是 any,联合类型会相当于 any
type e = unknown | any; // any
基础类型联合
let a: string | number;
a = 1; //ok
a= "a"//ok
对象类型联合
只能访问联合中 所有共同成员
interface Women{
age: number,
sex: string,
cry(): void
}
interface Man{
age: number,
sex: string,
}
declare function People(): Women | Man;
let people = People();
people.age = 18; // ok
people.cry();// error 非共同成员
交叉类型 &
多种类型的集合。每种类型都可以赋值给 unknown 类型,所以在交叉类型中包含 unknown 不会改变结果
type u1 = unknown & null; // null
type u2 = unknown & undefined; // undefined
type u3 = unknown & string; // string
type u4 = unknown & number[]; // number[]
type u5 = unknown & any; // any
interface People {
age: number
}
interface Man{
sex: string
}
const pengyuyan = (man: People & Man) => {
console.log(man.age) // 18
console.log(man.sex) // male
}
pengyuyan({age: 18,sex: 'male'})
同名基础属性合并
type obj = { a: string, c: number }
type obj2 = { b: number, c: string }
type allObj = obj & obj2
const Info: allObj = {
a: 'hello world',
b: 7,
// c 的类型既可以是 string 类型又可以是 number 类型。很明显这种类型是不存在的,所以混入后 c 的类型为 never
c: 1, // Type 'number' is not assignable to type 'never' c: never
c: 'hello', // Type 'string' is not assignable to type 'never' c: never
}
同名非基础属性合并
interface obj3 { a: number }
interface obj4 { b: string }
interface A {
x: obj3
}
interface B {
x: obj4
}
type obj5 = A & B
const Info: obj5 = {
x: {
a: 1,
b: 'hello'
}
}
console.log(Info) // { x: { "a": 1, "b": "hello" }}
四、interface 和 type 的区别
interface 只能声明 函数/对象
interface obj1 {
a: number
b: string
}
interface fn1 {
() => void
}
type 除了能声明 对象、函数,还可以为基础类型声明别名
type a = number | string
五、Class(类)
基本方法
在基本方法中有:静态属性,静态方法、成员属性、成员方法、构造器、get、set 方法
// 类定义方式如下
class obj{
// 类作用域
static myName: string = "pengyuyan" // 静态属性
name1:string = "男"
engine:string
engine3!:string //*在成员属性中,如果不给默认值,并且不使用是会报错的 。不设置默认值的时候加 ! 就不会报错 engine3!:string
// 构造函数
constructor(engine:string) {
this.engine = engine
}
// 静态方法
static staticFn = ()=>{
return '静态方法'
}
// 成员方法
fn2 = () =>{
return '成员方法'
}
// get方法
get engine2(){
return this.engine
}
// set方法
set engine2(engine2){
this.engine = engine2
}
}
const setObj = new obj('hello')
console.log(obj.myName) // "pengyuyan"
console.log(obj.staticFn()) // "静态方法"
console.log(setObj.fn2()) // "成员方法"
console.log(setObj.engine2) // "hello"
console.log(setObj.engine) // "hello"
// *set方法和get方法时的错误提示:
// error TS1056: Accessors are only available when targeting ECMAScript 5 and higher
// 解决方法:需要编译到 es5 及更高版本时可用
tsc 项目名称 -t es5
tsc Class(类).ts -t es5
// 类定义方式如下
var obj = /** @class */ (function () {
// 构造函数
function obj(engine) {
// 成员属性
this.name1 = "男";
// 成员方法
this.fn2 = function () {
return '成员方法';
};
this.engine = engine;
}
Object.defineProperty(obj.prototype, "engine2", {
// get方法
get: function () {
return this.engine;
},
// set方法
set: function (engine2) {
this.engine = engine2;
},
enumerable: false,
configurable: true
});
// 类作用域
obj.myName = "pengyuyan"; // 静态属性
// 静态方法
obj.staticFn = function () {
return '静态方法';
};
return obj;
}());
var setObj = new obj('hello');
console.log(obj.myName); // "pengyuyan"
console.log(obj.staticFn()); // "静态方法"
console.log(setObj.fn2()); // "成员方法"
console.log(setObj.engine2); // "hello"
console.log(setObj.engine); // "hello"
只读属性 readonly
- 只能在 构造函数中初始化,并且在TS中,只允许将 interface、type、class上的属性标识为 readonly
- readonly实际上只是在 编译阶段进行代码检查
- 被 readonly修饰的词只能在 constructor阶段修改,其他时刻不允许修改
class Person {
public readonly name:string; // 字符串 只读
name2:string
constructor(name:string){
this.name = name
this.name2 = name
}
setName(name:string) {
this.name = name // Cannot assign to 'name' because it is a read-only property.
this.name2 = name; // ok
}
}
const name3 = new Person('pengyuyan')
console.log(name3) // Person: {"name": "pengyuyan", "name2": "pengyuyan"}
console.log(name3.name2) // pengyuyan
console.log(name3.name) // pengyuyan
继承 extends
- 继承之后,子类会拥有父类的一切属性和方法
- 子类也可以自己定义一些方法,如 **getTel() **方法
- 子类也可以写与父类相同的方法,这样执行方法的时候会执行子类的方法,叫做方法重写( Child子类中重写了sayName方法)
// 父类
class Person {
name: string
age: number
constructor(name: string, age:number){
this.name = name
this.age = age
}
getName(){
console.log(`name是:${this.name}`)
return this.name
}
setName(name: string){
console.log(`设置name为:${name}`)
this.name = name
}
sayName(){
console.log(`哈哈哈,我是${this.name}`)
}
}
// 子类
class Child extends Person {
tel: number
// 如果在子类中写了构造函数,就必须调用父类的构造函数
constructor(name: string, age: number, tel:number){
super(name, age) // super用在子类中,表示当前的父类
this.tel = tel
}
// 子类也可以自己定义一些方法
getTel(){
return this.tel
}
sayName(){
// 类的方法中super就表示父类,可以通过super.(父类的方法)调用父类的方法
super.sayName()
console.log("Child")
}
}
let res = new Child("pengyuyan", 18 , 133123456789)
res.setName('pengyuyan') // 设置name为:pengyuyan
console.log(res) // Child {."name": "pengyuyan", "age": 18, "tel": 133123456789 }
console.log(res.age) // 18
console.log(res.getTel()) // 133123456789
res.sayName()
res.getName()
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qAZwCqZA-1661916600109)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/feff259796cb47029929efa7841c5e26~tplv-k3u1fbpfcp-zoom-1.image)]
修饰符
- public:类中、子类内的任何地方、外部 **都能调用 **
- protected:类中、子类内的任何地方都能调用,但 外部不能调用
- private:类中可以调用,子类内的任何地方、外部 均不可调用
class Person {
public name: string // 类中、子类内的任何地方、外部 都能调用
protected age: number // 类中、子类内的任何地方都能调用,但外部不能调用
private tel: number // 类中可以调用,子类内的任何地方、外部都不可调用
constructor(name: string, age:number, tel: number){
this.name = name
this.age = age
this.tel = tel
}
}
class Child extends Person {
constructor(name: string, age: number, tel: number) {
super(name, age, tel);
}
getName(){
console.log(`我的name是${this.name},年龄${this.age}`) // "我的name是pengyuyan,年龄18"
console.log(`电话是${this.tel}`) // 类中可以调用,子类内的任何地方、外部都不可调用
console.log(`年龄是${this.age}`) // 类中可以调用 "年龄是18"
}
}
const res = new Child('pengyuyan', 18, 133123456789)
console.log(res.name) // Domesy
console.log(res.age) // 类中、子类内的任何地方都能调用,但外部不能调用
console.log(res.tel) // 类中可以调用,子类内的任何地方、外部都不可调用
res.getName()
重写和重载
重写
继承父类之后重写父类的方法
class Person{
setName(name: string){
return `我的名字是${name}`
}
}
class Child extends Person{
setName(name: string){
return `我的名字是${name}`
}
}
const newInfo = new Child()
console.log(newInfo.setName('pengyuyan')) // "我的名字是pengyuyan"
重载
class double{
setNum(num: string);
setNum(num: number);
setNum(num:string | number){
if(typeof num === 'string'){
console.log(num + num)
}else{
console.log(num*2)
}
};
}
const res = new double()
res.setNum('1') // "11"
res.setNum(1) // 2
abstract
用 abstrac 关键字来声明一个 抽象类,抽象类中的方法声明的方法叫做 抽象方法。抽象类 不能被直接实例化,只能 被子类继承,而且需要重新实现抽象类中的抽象方法。abstract 还可以修饰属性和存取器(get、set)
abstract class Person {
constructor(public str: string){}
// 抽象类中的方法也必须是抽象方法
abstract numMethod(id: number) :void;
}
class Child extends Person {
constructor(str: string) {
super(str);
this.str = str
}
// 抽象类中的函数
numMethod(id: number): void {
console.log(`str 是${this.str},id 是${id}`);
}
}
let a = new Person("pengyuyan") // Cannot create an instance of an abstract class
let b = new Child("pengyuyan");
b.numMethod(7) // str 是pengyuyan,id 是7
六、断言
类型断言
可以手动指定一个值的类型,不需要ts 去判断。
*注意:在tsx中必须使用 **值 as 类型 **的语法。
// 值 as 类型
let str: any = 'pengyuyan';
let num: number = (str as string).length;
// <类型>值
let str2:any = 'pengyuyan'
let num2: number = (<string>str2).length; // 会报错 react
将任何一个类型断言为 any ( 不能滥用 as any)
let obj:object = {};
obj.num = 1; // error obj并没有num属性,所以就需要将 obj 断言为any
// 改为
(obj as any).num = 1;
// *注意:不能滥用as any
将any断言为一个具体的类型
let obj:object = {};
function fn1(key: string): any {
return (obj as any).name = key;
}
// fn1()方法执行以后返回的是any类型
interface Animal {
name:string;
fn2():void;
}
let a = fn1("pengyuyan") as Animal;
// 将其断言为Animal类型
将一个联合类型断言为其中一个类型
注意:类型断言只是欺骗编译器,让编译器可以编译通过,但是如果强制类型转换,在执行*的过程中会报错
interface obj1 {
name: string;
fn1(): void;
}
interface obj2 {
name: string;
fn2(): void;
}
function isObj(obj3: obj1 | obj2):string {
return obj3.name // 只能访问联合属性中共有的属性和方法
// 断言,强制类型转换会报错
return (typeof (obj3 as obj2).fn2) === 'function')
}
const obj4:obj1 = {
name:"obj1",
fn1(){
console.log("fn1xxxxx");
}
}
function fn3(animal:obj1|obj2){
(animal as obj2).fn2();
}
fn3(obj4); // animal.fn2 is not a function
将一个父类断言为更加具体的子类
//类有继承关系
class Father extends Error { //抽象的父类 Error,这样这个函数就能接受 Error 或它的子类作为参数了
num1: number = 1;
}
class Child extends Error {
num2: number = 2;
}
function fnType (error: Error) {
// 父类 Error 中没有 num1 属性,所以需要使用类型断言获取
if (typeof (error as Father).num1 === 'number') {
return true;
}
return false;
}
非空断言
let myName:string = 'pengyuyan'
console.log(myName.trim()) // 为undefined时候报错
// 改为
console.log(myName!.trim())
欢迎留言指正
学习资源:TypeScript 入门教程 一篇让你完全够用TS的指南
学习的代码整理好会同步到 gitlab