基础操作
作者:Choi Yang
更新于:6 个月前
字数统计:1.7k 字
阅读时长:7 分钟
阅读量:
null 与 undefined
在 JavaScript 中:
- null:这里有值,但是个空值
- undefined:这里没有值
在 TypeScript 中:
- null 与 undefined 类型都是有具体意义的类型
在没有开启
strictNullChecks
检查的情况下,会被视作其他类型的子类型。
typescript
const ans1: string = null; // 仅在关闭 strictNullChecks 时成立,下同
const ans2: string = undefined;
void 类型
在 TypeScript 中,undefined
类型是一个实际的、有意义的类型值,而 void
代表空的、没有意义的类型值。
typescript
// 没有 return 用 void
function foo(): void { }
// 有 return 但是没有返回值,用 undefined
function bar(): undefined {
return;
}
never 类型
never 类型不包含任何的类型,啥也没有,在联合类型中会被移除。
never 是整个类型系统层级中最底层的类型,即 Bottom Type
。
typescript
// never 类型使用
declare const strOrNumOrBool: string | number | boolean;
if (typeof strOrNumOrBool === "string") {
console.log("str!");
} else if (typeof strOrNumOrBool === "number") {
console.log("num!");
} else if (typeof strOrNumOrBool === "boolean") {
console.log("bool!");
} else {
// 未处理的新类型,都会走到 never 进行报错
const _exhaustiveCheck: never = strOrNumOrBool;
throw new Error(`Unknown input type: ${_exhaustiveCheck}`);
}
any 与 unknown
any 与 unknown 的本质是类型系统中的顶级类型,即 Top Type
。
unknown 类型可以赋值为任意其它类型,但它只能赋值给 any 或者 unknown 类型。
typescript
// any unknown
let anyVar: any = null;
anyVar.foo.bar.baz();
let unknownVar: unknown;
unknownVar.foo(); // 对象的类型为 "unknown"。ts(2571)
(unknownVar as { foo: () => {} }).foo(); // 当 unknown 类型,进行属性访问,需要类型断言
unknown 使用会比较麻烦,需要一堆的类型断言。
unique symbol
在 TypeScript 中,使用 unique symbol
(symbol 类型的子类型)确保独一无二。
typescript
// unique symbol
const uniqueSymbolFoo: unique symbol = Symbol("Chocolate")
// 不能将类型“typeof uniqueSymbolFoo”分配给类型“typeof uniqueSymbolBar”。ts(2322)
const uniqueSymbolBar: unique symbol = uniqueSymbolFoo
typescript
// 复用 unique symbol 类型
declare const uniqueSymbolA: unique symbol;
const uniqueSymbolB: typeof uniqueSymbolA = uniqueSymbolA
枚举
typescript
// 枚举
enum IEnum {
Home_Page_Url = 'https://yangchaoyi.vip/',
Blog_Page_Url = 'https://blog.yangchaoyi.vip/',
Num1 = 1999,
Num2
}
const num = IEnum.Num2;
console.log(num); // 2000
console.log(IEnum[1999]) // Num1
// 常量枚举
const enum IEnum2 {
Val1 = 99,
Val2
}
const val1 = IEnum2.Val1;
console.log(val1); // 99
函数
typescript
type Func = (name: string) => number
const foo: Func = (name) => {
return name.length
}
可选与只读
typescript
// 可选与只读属性
interface IProps {
readonly name: string;
age: number;
male?: boolean;
func?: Function;
}
const obj: IProps = {
name: 'Chocolate',
age: 18,
male: true,
// 无需实现 func 也是合法的
};
obj.name = 'HearLing'; // 无法分配到 "name" ,因为它是只读属性。ts(2540)
非空断言
typescript
// 非空断言
declare const foo: {
func?: () => ({
prop?: number | null;
})
};
foo.func!().prop!.toFixed();
// 等价于
((foo.func as () => ({
prop?: number;
}))().prop as number).toFixed();
object、Object 以及
记住以下几点规则:
- 不使用 Object 以及类似的装箱类
object 的引入就是为了解决对 Object 类型的错误使用,它代表所有非原始类型的类型,即数组、对象与函数类型。
- 当不确定某个变量的具体类型,但能够确定它不是原始类型,可以使用 object。可以进一步区分,例如用
Record<string, unknown>
或Record<string, any>
表示对象,unknown[]
或any[]
表示数组,(...args: any[]) => any
表示函数。 {}
意味着任何非null / undefined
的值,尽量避免使用。
字面量类型
字面量类型主要包括字符串字面量类型、数字字面量类型、布尔字面量类型和对象字面量类型。
typescript
// 字面量类型
const str: "Chocolate" = "Chocolate";
const num: 1999 = 1999;
const bool: true = true;
字面量类型要求的是值级别的字面量一致,因此比原始值类型更精确。 对象字面量类型就是一个对象类型的值,对象的值都为字面量值,不常用。
通常与联合类型一起使用:
typescript
// 字面量与联合类型
interface Iprops {
bool: true | false;
num: 1 | 2 | 3;
str: "RNG" | "EDG" | "TES" | "JDG"
}
let 与 const 使用区别:
- let 只需推导对应的从属类型即可
- const 声明的原始类型变量将不再可变,因此类型会收窄到最精确的字面量类型(但对象类型变量仍可变)
typescript
// let 与 const
let userName = 'Chocolate'; // let userName: string
const userAge = 20; // const userAge: 20
联合类型
typescript
// 联合类型
interface Iprops {
props: true | string | 1999 | {} | (() => {}) | (1 | 2 | 3)
}
联合类型中的函数要用
()
包裹一下,因为函数并不存在字面量类型。
typescript
// 多个对象类型的联合,实现手动的互斥属性
interface IUser {
user:
| {
vip: true;
expires: string;
}
| {
vip: false;
promotion: string;
};
}
declare var userInfo: IUser;
if (userInfo.user.vip) {
console.log(userInfo.user.expires);
}
索引类型
typescript
// 索引类型
type AllStringTypes = {
[key: string]: string;
}
const foo: AllStringTypes = {
"aaa": "123",
1999: "Chocolate",
[Symbol("sss")]: 'symbol',
}
interface Foo {
propA: number;
propB: boolean;
propC: string;
}
// 索引类型查询
type Keys = keyof AllStringTypes; // string | number
// 索引类型访问
type PropAType = Foo['propA']; // number
type PropBType = Foo['propB']; // boolean
type PropTypeUnion = Foo[keyof Foo]; // string | number | boolean
type 与 interface
推荐做法:
tinterface 用来描述对象、类的结构。
type 将一个函数签名、一组联合类型、一个工具类型等等抽离成一个完整独立的类型。
不过大部分场景下接口结构都可以被类型别名所取代,简而言之,能用就行。
类型查询操作符 typeof
typescript
// 类型查询操作符 typeof
const name = "Chocolate";
const obj = { name: "Chocolate" };
const nullVar = null;
const undefinedVar = undefined;
const func = (input: string) => {
return input.length > 10;
}
type Str = typeof str; // "Chocolate"
type Obj = typeof obj; // { name: string; }
type Null = typeof nullVar; // null
type Undefined = typeof undefined; // undefined
type Func = typeof func; // (input: string) => boolean
类型守卫
typescript
// 类型守卫
export type Falsy = false | "" | 0 | null | undefined;
export const isFalsy = (val: unknown): val is Falsy => !val;
// 不包括不常用的 symbol 和 bigint
export type Primitive = string | number | boolean | undefined;
export const isPrimitive = (val: unknown): val is Primitive => ['string', 'number', 'boolean' , 'undefined'].includes(typeof val);
类型断言守卫
typescript
// 类型断言守卫
const name: any = 'Chocolate';
function assertIsNumber(val: any): asserts val is number {
if (typeof val !== 'number') {
throw new Error('not a number!');
}
}
assertIsNumber(name);
// number 类型!
name.toFixed();
infer 关键字
typescript
// infer 关键字
type Swap<T extends any[]> = T extends [infer A, infer B] ? [B, A] : T;
type SwapResult1 = Swap<[1, 2]>; // 符合元组结构,首尾元素替换 [2, 1]
type SwapResult2 = Swap<[1, 2, 3]>; // 不符合结构,没有发生替换,仍是 [1, 2, 3]
// 提取首尾两个
type ExtractStartAndEnd<T extends any[]> = T extends [
infer Start,
...any[],
infer End
]
? [Start, End]
: T;
// 调换首尾两个
type SwapStartAndEnd<T extends any[]> = T extends [
infer Start,
...infer Left,
infer End
]
? [End, ...Left, Start]
: T;
// 调换开头两个
type SwapFirstTwo<T extends any[]> = T extends [
infer Start1,
infer Start2,
...infer Left
]
? [Start2, Start1, ...Left]
: T;
// 提取对象的属性类型
type PropType<T, K extends keyof T> = T extends { [Key in K]: infer R }
? R
: never;
type PropTypeResult1 = PropType<{ name: string }, 'name'>; // string
type PropTypeResult2 = PropType<{ name: string; age: number }, 'name' | 'age'>; // string | number
// 反转键名与键值
type ReverseKeyValue<T extends Record<string, unknown>> = T extends Record<infer K, infer V> ? Record<V & string, K> : never
type ReverseKeyValueResult1 = ReverseKeyValue<{ "key": "value" }>; // { "value": "key" }
上下文类型
typescript
// 上下文类型
type CustomHandler = (name: string, age: number) => boolean;
const handler: CustomHandler = (arg1, arg2) => true;
// 基于位置的类型推导
declare const struct: {
handler: CustomHandler;
}
struct.handler = (name, age) => { }; // 不能将类型“void”分配给类型“boolean”。
void 使用
typescript
// 通过 void 执行立即执行函数
void function iife() {
console.log("log!");
}();
// 等价于↓
void ((function iife() { })())
具名元组
typescript
// 具名元组
const arr: [name: string, age?: number, male?: boolean] = ['Chocolate', 18, true];
type TupleLength = typeof arr.length; // 1 | 2 | 3