Typescript 的优点
- 程序更容易理解;例如使用函数和对象时有提示的入参和属性等
- 开发效率更高;跳转定义很方便,代码补全等
- 更少的低级错误;结合TS的一些校验规则,基本排除了一些变量名写错等低级错误
- 兼容性好;可以最快速的使用 ECMA 提供的最新的特性,而不用考虑兼容问题
原始类型
// Boolean
const isShow: boolean = false
// Null 在 MDN 中的 javascript 数据类型中,null是一种单独的类型,不属于原始类型 typeof null === 'object'
const n: null = null
// Undefined
const u: undefined = undefined
// Number
const age: number = 18
// BigInt 创建 BigInt 类型的值也非常简单,只需要在数字后面加上 n 即可
let time: bigint = 123n
let time: bigint = BigInt(123)
BigInt(123) === 123n // true
// String
const str: string = 'name'
// Symbol 表示独一无二的值
const s1: symbol = Symbol('Hello')
// undefined 和 null 是所有类型的子类型
const num: number = null
其他类型
Any
const notSure: any = 4
Unknown
const notSure: unknown = 4
any 和 unknown 的区别: any 能赋值给任意类型的变量,unknown 类型的值不能赋值给 any 和 unknown 以外的类型变量。
Array
const arr: number[] = [1, 3, 6]
Tuple 元组
const user : [string, number] = ['Joke', 18]
user.push(24) // 只能push元组已定义的类型
Interface
对对象、函数、类的形状进行描述(duck typing)
interface Person {
name: string,
age: number,
readonly sex: number, // 只读属性
id ? : string // ?表示可有可无
}
const Job : Person = {
name: 'Job',
age: 26,
sex: 1
}
Function
function add(x : number, y : number, z?: number) : number {
return x + y
}
const res = add(1, 2)
const add2 : (x : number, y : number) => number = add // 函数赋值
let add3: (x: number, y: number, z: number) => number = add // add2 和 add3 的类型都是 add 的类型的子集,就像 null 是所有类型的子集
// 使用 Interface 描述函数
interface Sum {
(x : number, y : number) : number
}
let add3 : Sum = add
联合类型 union-types
let aa : number | string = 23
aa = 'aa'
aa.length // 报错 因为Number没有length方法
类型断言 使用联合类型时,若断言了类型,即可使用其 API
告诉编译器: “相信我,我知道自己在干什么”
function getLength (input: string | number): number {
const str = input as string
if (str.length) {
return str.length
}
const num = input as number
return num.toString().length
}
function getLength (input: string | number): number {
if (typeof input === 'string') { // 也是断言的一种
return input.length
} else {
return input.toString().length
}
}
function isFish(animal: Cat | Fish): boolean {
// 将 animal 断言成 Fish 类才能访问 swim 方法
if (typeof (animal as Fish).swim === 'function') {
return true
}
return false
}
// 尖括号断言
// let someValue: any = 'some str';
// let strLength: number = (<string>someValue).length
// 谓词断言
function unknownToString(data: unknown): data is string {
return typeof data === 'string'
}
const aa: unknown = [123,33]
if(unknownToString(aa)){
const bb = aa // aa => string
}
注意:断言只能够‘欺骗’编译器不报错,无法避免运行时的报错,所以滥用断言很可能导致运行时错误
理想情况下,TypeScript 的类型系统运转良好,每个值的类型都具体而精确。 当我们引用一个在此类型上不存在的属性或方法时,就会报错,但有的时候,我们非常确定这段代码不会出错,比如下面这个例子:
window.foo = 1;
// index.ts:1:8 - error TS2339: Property 'foo' does not exist on type 'Window & typeof globalThis'.
此时我们可以使用 as any
临时将 window
断言为 any
类型:(window as any).foo = 1;
有时候我们也需要将 any
断言为一个具体的类型,例如:
function getCacheData(key: string): any {
return (window as any).cache[key];
}
interface Cat {
name: string;
run(): void;
}
const tom = getCacheData('tom') as Cat;
tom.run();
我们调用完 getCacheData
之后,立即将它断言为 Cat
类型。这样的话明确了 tom
的类型,后续对 tom
的访问时就有了代码补全,提高了代码的可维护性。
总结:
- 联合类型可以被断言为其中一个类型
- 父类可以被断言为子类
- 任何类型都可以被断言为 any
- any 可以被断言为任何类型
Class 类
class Person {
constructor () {}
add () {} // 默认public,本类不可访问不可修改,子类不能访问不能修改,实例可访问可修改
static run () {} // 静态属性或方法,不需要实例化,可直接通过类调用 Person.run() ,但是不能通过 this. 的方式访问修改,子类类名可访问可修改(子类也是不实例化的时候访问修改),实例不能访问不能修改
private sum () {} // 私有属性或方法,子类中不能访问不能修改,实例不能访问和修改(仅编译报错,实际执行可访问可修改)
protected aaa () {} // 受保护的属性和方法,子类中可访问可修改,实例不能访问和修改(仅编译报错,实际执行可访问可修改)
readonly ccc // 只读属性,只能作用于属性,本类实例可访问不能修改,子类中可访问不能修改,实例可访问不能修改
}
// 除 static 外,其他类型的子类指的都是子类中通过 this. 的方式访问和修改
implements
抽象类的属性和方法 —— 可以继承多个类,interface 只定义方法名,类中必须要实现 implements 的方法
interface Radio {
switchRadio (bool: boolean): void
}
interface Battery {
checkStatus (): void
}
class Car implements Radio {
switchRadio () {}
}
class Phone implements Radio, Battery {
switchRadio () {}
checkStatus () {}
}
Enum 枚举
enum Week {
first = 1,
tues = 2,
wes = 3
}
console.log(Week.tues)
console.log(Week[2])
enum Status { // 默认按索引值从0开始递增 0,1,2
accept,
follow,
saved
}
type alias 类型别名
type PlusType = (x: number, y: number) => number
let add: PlusType
type Position<T> = {x: T, y: T}
const p1 : Position<number> = {
x: 3,
y: 5
}
const p2: Position<string> = {
x: 'xxx',
y: 'yyy'
}
// 1. 类型别名无法被 extends 和 implements,所以类型需要拓展时,需使用接口;
// 2. 当无法通过接口,并且需要使用联合类型或者元组类型时,用类型别名;
字面量
type Direct = 'up' | 'down' | 'right' | 'left'
const move : Direct = 'down'
Generics 泛型
定义时不指定类型,使用时再判断类型
function echo<T>(aa: T): T{
return aa
}
const str : string = 'asd'
const res = echo(str) // res的类型由传入的参数的类型决定
// 约束泛型
interface IWithLength {
length: number
}
function echoWithLength<T extends IWithLength>(aaa: T): T {
console.log(aaa.length)
return aaa
}
使用泛型时,我们应该遵循两个标准: 1.当函数、接口或类处理各种数据类型时 2.当函数、接口或类在多个位置使用该数据类型时
// 泛型类
class Person<T>{
private _value: T;
constructor(val: T) {
this._value = val;
}
}
let p = new Person<number>(12)
// 泛型函数
function fn<T>(arg: T): T {
return arg;
}
fn<number>(12); // 编辑器可以推断传入的参数,可以省略 <number>
// 定义接口
interface Identities<V, M> {
value: V,
message: M
}
let identities: Identities<number, string> = {
value: 123,
message: 'msg'
}
// 定义箭头函数
const foo = <T>(x: T): T => x
如何在 vue 文件中定义组件 ref
const xxxRef = ref<InstanceType<typeof ComponentName> | null>(null)
TS 扩展 window
// global.d.ts
declare global {
interface Window {
xxx: string
}
}
export {}
之后检查 global.d.ts
是否在 tsconfig.json
的 include
中