简单类型
基本类型
- number
- string
- boolean
- never
- void
- object
- symbol
- bigint
- …
let num: number = 1;
let str: string = '1';
let bol: boolean = true;
let s1: symbol = Symbol(1);
let max = Number.MAX_SAFE_INTEGER
let r1:bigint = BigInt(max);
// never 永远打不到的类型
function throwError(): never {
throw new Error()
}
function setVal(val: string) {
if (typeof val === 'string')
else val // never
}
function whileTrue(): never {
while (true) { }
}
// void 一般用于函数无返回值
function getVoid():void {...}
联合类型
多个类型的集合
// 类型联合
let numOrStr: number | string = '1';
numOrStr = 1;
// 固定值联合
type IType = 'a' | 'b' | 'c' | 'd'
let type: IType = 'a';
type = 'b';
函数类型
函数可以定义参数类型和返回值类型, 也可以给函数本身添加类型,两方添一方即可,会自动推导类型
// 自动推导sum的类型
function sum(x: number, y: string): string { // 函数 括号后面的是返回值类型
return x + y;
}
// 自动推导参数和返回值类型
type IFn = (a: number, b: number) => number
const sum1: IFn = (x,y) => x + y;
// 多余参数用扩展运算符
const sum3 = (x: number, y?: number, ...args: number[]): number => {
return x + (y as number);
}
// 函数重载 重载方法在真实方法的上面
function toArray(value:string):string[]
function toArray(value:number):number[]
function toArray(value:string | number) {
......
}
类的类型
类的修饰符
- public 是属性修饰符 public 表示自己 和 子类 和子类之外都可以访问到
- protected 只有自己和 自己的后辈能访问
- private 就是只有自己能访问的属性
类中的装饰器: 修饰类会将target 目标类传给定义的函数 修饰属性会将目标类和属性key传给定义好的函数 修饰方法 。。。 修饰参数 。。。
@addSaay
class Person {
// eat!:()=>void
@toUpperCase
public name: string = 'jw';
@double(3)
static age: number = 18; // 静态的通过类来调用
@Enum(false)
// 还能修饰参数
drink(@params content: any) { }
}
function double(num: number) {
return function (target: any, key: string) { // target => 类
let v = target[key]
Object.defineProperty(target, key, {
get() {
return num * v
}
})
}
}
接口
使用interface声明一个接口,用来描述对象的结构 也可以使用type来定义接口
- 区别,interface可以被类实现和继承,type可以使用联合类型
- 多个重名接口会合并
// kv之间可以用逗号分隔,也可以: 也可以无符号回车
interface IObj {
(params: string): string // 函数
readonly read: boolean // 仅读
name: string // 必填
age?: number // 选填
[key: string]: any // 扩展任意值
}
interface My extends IObj {} // 继承
接口实现: 使用implements关键字继承,可继承多个接口,用逗号隔开
interface ISpeakable {
name: string,
speak(): void
}
interface IChineseSpeakable {
speakChinese(): void
}
class Speak implements ISpeakable, IChineseSpeakable {
speakChinese(): void {
throw new Error("Method not implemented.");
}
name!: string
speak(): string { // 此方法是原型方法
return 'xxx'
}
}
泛型
泛型的用处在于 当我们调用的时候 确定类型,而不是一开始就写好类型,类型不确定,只有在执行的时候才能确定
- 泛型可以在 函数 类 (接口、别名) 中使用
- extends 约束
- keyof 取当前类型的key
- typeof 取当前值的类型
// 接口使用泛型
interface IMyArr<T> {
name: T
}
let my: IMyArr<string> = {
name: 'str'
}
// 函数使用泛型
const swap = <T, K>(tuple: [T, K]): [K, T] => { // 元组是特殊的数组
return [tuple[1], tuple[0]]
}
类的约束
// 必须包含length属性
type withLen = { length: number }
const computeArrayLength = <T extends withLen, K extends withLen>(arr1: T, arr2: K): number => {
return arr1.length + arr2.length
}
computeArrayLength('123', { length: 3 })
const getVal = <T extends object, K extends keyof T>(obj: T, key: K) => {
if (typeof obj !== 'object') {
return
}
}
keyof
keyof { a: 1, b: 2 } // => 'a' | 'b'
keyof any // => number | string | symbal
keyof string // => string的属性和方法对应的key
类型保护
js类型判断方法
- typeof
- instanceof
- in语法
// typeof
function fn(val: string | number) {
if (typeof val == 'string') {
val.match
} else {
val.toFixed
}
}
// instanceof
class Person { eat() { } }
class Dog { }
const createClass = (clazz: new () => Person | Dog) => {
return new clazz
}
let r = createClass(Person);
if (r instanceof Person) {
r.eat // Person
} else {
r // Dog
}
is语法
// 用于判断方法,断言返回值的类型,不关心方法内容
function isString(val: any): val is string { // 根据函数的返回值确定是不是string类型
// ts 是给代码的 js是自己的逻辑 ts不关心,ts只关心类型
return Object.prototype.toString.call(val) == '[object String]'
}
完整性保护
const assert = (obj: never) => { throw new Error("err"); }
// 完整性保护 保证代码逻辑全部覆盖到
function getArea(obj: ISquare | IRant | ICircle) {
switch (obj.kind) {
case 'square':
return obj.width * obj.width;
case 'rant':
return obj.width * obj.height;
case 'circle':
return
default:
assert(obj);
}
}
getArea({ kind: 'circle', r: 10 });
交叉
交叉类型 = 交集
- 可用于在原有类型基础上扩展类型
- 交叉类型可以赋值给没有交叉之前的类型
interface Person1 {
handsome: string,
// a:string 如果两个类型不一致 则相交的结果是never
}
interface Person2 {
height: string,
// a:number
}
type Person3 = Person1 & Person2; // | 并集 & 交集 (交集可以理解成 涵盖所有属性)
let person: Person3 = {
handsome: '帅',
height: '高',
}
内置类型
条件类型
- Exclude:ts中内置的类型 内置类型包含条件的情况 (内部用条件来实现的)
- Extract:多个属性中 抽离某几个
- NonNullable:在多个类型中排除null类型(undefined也会被排除)
type Exclude<T, K> = T extends K ? never : T; // 在多个类型中提排除掉某几个
type MyExclude = Exclude<string | number | boolean, boolean> // string | number
type MyExtract = Extract<string | number | boolean, boolean> // => boolean
type NonNullable<T> = T extends null | undefined ? never : T; // 在多个类型中提排除掉某几个
type MyNonNullable = NonNullable<string | number | null | undefined> // string | number
- ReturnType: 获取返回值类型
- Parameters: 获取参数类型
- ConstructorParameters: 获取类的参数类型
// infer放在哪里 就是推断哪里的结果
function getSchool(x: string, y: number) {
return { name: 'jiatengda', age: 12 }
}
type ReturnType<T extends ((...args: any[]) => any)> = T extends ((...args: any[]) => infer R) ? R : any
type MyReturnType = ReturnType<typeof getSchool>; // {name: string;age: number;}
type Parameters<T extends ((...args: any[]) => any)> = T extends (...args: infer P) => any ? P : any
type MyParameters = Parameters<typeof getSchool>; // [x: string, y: number]
// ------------
class Person {
constructor(name:string){}
}
type ConstructorParameters<T extends new (...args:any[])=> any> = T extends new (...args:infer CP)=> any ? CP:any
type MyConstructorParameters = ConstructorParameters<typeof Person> // [name: string]
其他内置类型
- Partial: 表示选项可以是选填的 , 深度递归 ,默认不是深度递归
- Required -? 去掉可选
- Readonly 全部只读
- Pick 精挑细选 (对象里选属性) extract 抽离可用的 (类型中选择类型)
- Omit 忽略属性
interface ICompany {
name: string,
address: string
}
interface IPerson {
name?: string,
age: number,
company?: ICompany
}
// 内置类型默认不递归,在内置类型上扩展
type Partial<T> = { [K in keyof T]?: T[K] extends object?Partial<T[K]>:T[K]}
type MyPerson = Partial<IPerson>
/*
MyPerson = {
name?: string | undefined;
age?: number | undefined;
company?: ICompany | undefined;
}
*/
type Required<T> = { [K in keyof T]-?: T[K] }
type MyRequired = Required<MyPerson>
type Readonly<T> = { readonly [K in keyof T]: T[K] }
type MyReadonly = Readonly<IPerson>
type Pick<T, K extends keyof T> = { [X in K]: T[X] }; // 挑选属性
type MyPick = Pick<IPerson, 'age' | 'company'>
// 可以忽略再加上 修改新增的属性类型值等
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>
type MyType = Omit<IPerson, 'name'> & { name: string };
根据内置类型 自定义类型
- 差集 获取两个类型的差集 exclude 在一群类型中忽略掉某个类型 和 omit 对象中忽略
- 交集
- 对象合并
let person1 = {
name: 'jiatengda',
age: 12,
address: '回龙观'
}
let person2: {
address: '回龙观'
}
type Diff<T extends object, K extends object> = Omit<T, keyof K>;
type myDiff = Diff<typeof person1, typeof person2>;
/*
{
name: string;
age: number;
}
*/
// 交集
type Inter<T extends object, K extends object> = Pick<K, Extract<keyof T, keyof K>>
type myInter = Inter<typeof person1, typeof person2>;
/*
{
address: '回龙观';
}
*/
// 将类型展开
type Compute<T> = { [K in keyof T]: Compute<T[K]> }; // 将类型展开方便提示
// 对象合并
type Merge<T extends object, K extends object> = Omit<T, keyof K> & K;
type myMerge = Compute<Merge<Person1, Person2>>;
命名空间
为了解决命名冲突,称之为文件内的模块,一般开发中都是使用es6module、cjs 进行模块开发
关键字: namespace module,两个关键字都可以声明命名空间
- 命名空间就是通过自执行函数来是实现的,我们一般写代码不会使用
- 定义的内容需要export导出
- 两个重名的命名空间会合并,但是合并后重名的会报错
- 命名空间可以进行无线嵌套
namespace Home1 {
//export class Dog{} // 命名空间中的内容 需要导出
export const b = 'abc'
export namespace Graden{
export const a = '花园';
}
}
声明文件
何时使用声明文件?
- 引入第三方模块时,第三方模块不是用ts写的
- cdn引入,声明全局变量
- 给内置类原型扩展属性方法时
一般第三方模块的ts声明文件都会在@types/xxx 的包中维护
declare let a:string;
declare function fn():void;
declare namespace A {
const a:string // declare中的内容 不需要默认导出,也不用declare了
}
// 声明模块
declare module '*.vue' {
const component:object;
export default component
}
declare module '*.jpg'{}
声明命名可能会冲突
- 接口同名默认会合并
- 命名空间也能合并
- 函数和命名空间能合并