【TS基础】个人学习记录6-零碎概念之类型推论、联合类型、类型断言、类型别名、字符串字面量、交叉类型

类型推论

let str = 'str'
console.log(typeof str)

当没有指定变量类型的时候,ts会自动去推导类型。


联合类型

let numOrStr: number | string // 这个变量可以是数字也可以是字符串
numOrStr = 'abc'
numOrStr = 123
// 联合类型还可以直接时字符串
let str : 'a' | 'b' | 'c'
str = 'b'

在函数入参内使用联合类型的时候,要注意这样的变量只能访问联合类型的共有属性和方法:

// 函数作用,当入参类型是字符串时,返回长度
fuction getLength(val: string | number): number {
	// 因为length属性不是字符串和数字的共同属性,当你直接return val.length时,ts就会报错提示你,可能val为number类型没有length属性
}

这时就需要类型断言去解决了。


类型断言

它建议编译器将变量可以视为某种类型。

有两种写法:

as 类型 // 推荐写这种,因为更符合JSX的写法
<类型>

主要是为了解决一些类型判断的问题,例如以下。

联合类型的断言

前面说到联合类型的一个小缺点的解决方法。

fuction getLength(val: string | number): number {
	const str = val as string // 注意并不是类型转换,而是当做某类型去看
	if (str.length) { // 如果是数字类型是没有length属性的
		return str.length
	} else {
		const num = val as number
		return num.toString().length
	}
}
// 或(推荐)
fuction getLength(val: string | number): number {
	if (typeof val === 'string') {
		return val.length
	} else {
		return val.toString().length
	}
}

还可以结合接口:

interface Cat {
    name: string;
    run(): void;
}
interface Fish {
    name: string;
    swim(): void;
}

function isFish(animal: Cat | Fish) {
    // if (typeof animal.swim === 'function') { 这样写会报错,因为不一定会传Fish类型进来
    //    return true;
    // }
    if (typeof (animal as Fish).swim === 'function') { // 这样就给编译器指定参数为Fish类型,就不会报错了
        return true;
    }
    return false;
}

继承断言

class ApiError extends Error {
    code: number = 0;
}
class HttpError extends Error {
    statusCode: number = 200;
}

function isApiError(error: Error) {
    if (typeof (error as ApiError).code === 'number') {
        return true;
    }
    return false;
}

主要解决这样一个场景:

interface ApiError extends Error {
    code: number;
}
interface HttpError extends Error {
    statusCode: number;
}

function isApiError(error: Error) {
    if (error instanceof ApiError) { // 因为编译成es5后,接口的定义被去掉了,此时instanceof找不到ApiError所以报错
        return true;
    }
    return false;
}

可访问不存在属性

window.foo = 1;  // window 上添加一个属性 foo时,TS会报错,说不存在这个属性
(window as any).foo = 1;  // 断言成any类型后就可以添加了

注意:还是不推荐用any来操作

断言为具体接口类型

未来补充

补充

前面解释了个人对断言的理解是:它建议编译器将变量可以视为某种类型。

所以并不是所变量一定要符合as后面的类型才能接着往下走:

interface P {
  a: number;
}
function fn(x: object) {
  if (x as P) {
    return console.log("断言成功");
  } else {
    return console.log("不走断言");
  }
}
fn({ b: "1" }); // "断言成功" 记住啦

类型别名

就是给类型的定义取个名

type strOrNum = string | number // strOrNum 这个类型别名就包含了string或者number
let str: strOrNum = '123'
// 等同于
let str: string | number = '123'

通常用于给一些定义做一个打包!

例如定义了一个数组子项的接口类型,可以这样打包成一个数组类型:

interface element {
	name: string
}
type Arr = element[]

结合联合类型也是有很好的使用场景:

type gender = "man" | "women"
type age = "young" | "old"
type person = gender | age // 类型就综合了"man" | "women" | "young" | "old"

很多时候,类型别名都可以代替接口,因为方便,而且可以直接声明元组、联合类型、交叉类型、原始类型,也包括对象。

但是与接口不同的是,接口在同一个地方可以多次定义,定义的内容会累加起来,而同一个类型别名就不能多次被定义,会报错。

interface Language {
  id: number;
}
interface Language {
  name: string;
  // id: number 不能和上面有同样的哦,会报错
}
let lang: Language = {
  id: 1, 
  name: 'name' 
} // 这样是没问题的

// 如果把interface换成type就会报错

字符串字面量

例如:

let str: 'name' = 'name' // 等于name以外的都会报错
let num : 2 = 2 // 等于2之外的都会报错

有限定字符串的作用,当配合类型别名后可以这样

type Directions = 'Up' | 'Down' | 'Left' | 'Right'
let toWhere: Directions = 'Up' // 这个变量的值被限定在Directions里面这四个了,写成其他的会报错

或者配合接口:

interface tools = {
	type: 'stick' | 'scissors' | 'knife'
}
// 根据这个接口创建的变量,type属性只能写上面那三个。

等等。


交叉类型

感觉这个名字取得不好,容易误解,叫合并类型还稍微贴切点。

正确示范:

type s1 = {
    a: number
}
type s2 = {
    b: string
}
type ss = s1 & s2
let obj : ss = { a: 3, b: '1' } // 少写一个属性都报错

type num1 = '1' | '2' | '3';
type num2 = '2' | '3' | '4';
type num3 = num1 & num2;  
let numvar : num3 = '2' // 只能设置为2、3

错误示范:

type what = string & number // 意思是这个类型别名既是string 类型又是 number 类型,但我们知道这个是不存在的,所以ts把what 定义为了never

合并接口

那这个交叉类型有什么用处呢?

我们可以用来合并接口

interface IName {
	name: string
}
type Iperson = IName & { age: number }
// 这时候Iperson就变成
interface Iperson {
	name: string
	age: number
}

注意!合并的时候要看里面是不是存在不同类型合并出现never的问题:

interface A {
	name: string,
	age: number
}
type B = A & { name : number } // B的name和A的name又交叉合并了一次,变成never了,当然如果name都是同一种类型合并后还是同一种类型。不同的字符串字面量也是一样的哦。

是不是达到和继承接口一样的效果。

交叉联合类型

// 错误
type A = "a" | "A";
type B = "b" | "B";
type AB = A & B;
let ab: AB = "a"; // 报错,A与B没有交集

type A = "a" | "A";
type B = "a" | "B";
type AB = A & B; // 交集为a,所以 AB类型为 'a' 
let ab: AB = "a"; // 正常赋值

当&和|同时使用时,&的优先级大于|。这里就不举例了。


类型缩减

例如:

type BorderColor = 'black' | 'red' | 'green' | 'yellow' | 'blue' | string; // 前面的因为都属于string类型,最后面的又是个string类型,所以整体缩减成 string

这样,编辑器就不会提示BorderColor里有'black' | 'red' | 'green' | 'yellow' | 'blue'这些东西了。解决办法是类型后面加个符号:

type BorderColor = 'black' | 'red' | 'green' | 'yellow' | 'blue' | string & {}; // 字面类型都被保留

其实这个还不是重要的,重要的是当联合类型的成员是接口类型,如果满足其中一个接口的属性是另外一个接口属性的子集,这个属性也会类型缩减!

type A =
  | {
      age: "1";
    }
  | {
      age: "1" | "2";
      [key: string]: string;
    };
let a = { age: "2" }; // 此时age被缩减为"1" | "2"了,怕不怕

平时要注意这个问题。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值