当前位置: 首页 > news >正文

access数据库网站开发百度上看了不健康的内容犯法吗

access数据库网站开发,百度上看了不健康的内容犯法吗,怎么制作移动端网站,小程序有哪些开发平台推荐一下小册 TypeScript 全面进阶指南,此篇笔记来源于此,记录总结,加深印象! 另外,如果想了解更多ts相关知识,可以参考我的其他笔记: vue3ts开发干货笔记TSConfig 配置(tsconfig.…

推荐一下小册 TypeScript 全面进阶指南,此篇笔记来源于此,记录总结,加深印象!

另外,如果想了解更多ts相关知识,可以参考我的其他笔记:

  • vue3+ts开发干货笔记
  • TSConfig 配置(tsconfig.json)
  • ts相关笔记(Partial、Required、Readonly、Record、Exclude、Extract)
  • ts相关笔记(类型层级)
  • ts相关笔记(extends、infer、Pick、Omit)

原始类型和对象类型

原始类型

除了最常见的 number / string / boolean / null / undefined, ES6、ES11)又分别引入了 2 个新的原始类型:symbol 与 bigint

const name: string = 'ts';
const age: number = 18;
const male: boolean = false;
const undef: undefined = undefined;
const nul: null = null;
const obj: object = { name, age, male };
const bigintVar1: bigint = 90071992547409212n;
const bigintVar2: bigint = BigInt(9007199254740991);
const symbolVar: symbol = Symbol('unique');

null和undefined

  • 在js里,null表示一个空值,undefined表示没有值
  • 在ts里,null 与 undefined 类型都是有具体意义的类型

在没有开启strictNullChecks 检查的情况下,这两种类型会被视作其他类型的子类型,比如 string 类型会被认为包含了 null 与 undefined 类型:

const tmp1: null = null;
const tmp2: undefined = undefined;const tmp3: string = null; // 仅在关闭 strictNullChecks 时成立,下同
const tmp4: string = undefined;

void

用于描述一个内部没有 return 语句,或者没有显式 return 一个值的函数的返回值,如:

function func1() {}
function func2() {return;
}
function func3() {return undefined;
}

数组类型标注

const arr1: string[] = []; // 更推荐此写法const arr2: Array<string> = [];
元组(Tuple)

元组就是类型和数据的个数一开始就已经限定好了,某些情况下使用元组代替数组要更加妥当。
下面是一些应用

  • 提供越界提醒

    const arr3: string[] = ['aaa', 'bbb', 'ccc']
    const arr4: [string,string,string] = ['aaa', 'bbb', 'ccc']
    console.log(arr4[4])
    

    在这里插入图片描述

  • 可选

    const arr6: [string, number?, boolean?] = ['aaa'];
    // 下面这么写也可以
    // const arr6: [string, number?, boolean?] = ['aaa', , ,];
    
  • 具名元组
    在 TypeScript 4.0 中,有了具名元组(Labeled Tuple Elements)的支持,使得我们可以为元组中的元素打上类似属性的标记:

    const arr7: [name: string, age: number, male?: boolean] = ['aaa', 18, true];
    

对象类型标注

使用interface接口
interface IDescription {name: string;age: number;male: boolean;
}const obj1: IDescription = {name: 'aaa',age: 599,male: true,
};

可选 ?

interface IDescription {name: string;age: number;male?: boolean;func?: Function;
}const obj2: IDescription = {name: 'aaa',age: 599,male: true,// 无需实现 func 也是合法的
};

只读

interface IDescription {readonly name: string;age: number;
}const obj3: IDescription = {name: 'aaa',age: 599,
};// 无法分配到 "name" ,因为它是只读属性
obj3.name = "AAA";
使用type 类型别名
type User = {name: stringage: number
};

很多人更喜欢用 type(Type Alias,类型别名)来代替接口结构描述对象,
而更推荐的方式是

  • interface 用来描述对象、类的结构
  • type类型别名用来将一个函数签名、一组联合类型、一个工具类型等等抽离成一个完整独立的类型

但大部分场景下接口结构都可以被类型别名所取代,取决于个人喜好或者团队的一些规范吧。

object、Object 以及 { }

Object

js中,原型链的顶端是 Object 以及 Function,这也就意味着所有的原始类型与对象类型最终都指向 Object,在 TypeScript 中就表现为 Object 包含了所有的类型

// 对于 undefined、null、void 0 ,需要关闭 strictNullChecks
const tmp1: Object = undefined;
const tmp2: Object = null;
const tmp3: Object = void 0;const tmp4: Object = 'aaa';
const tmp5: Object = 599;
const tmp6: Object = { name: 'aaa' };
const tmp7: Object = () => {};
const tmp8: Object = [];

和 Object 类似的还有 Boolean、Number、String、Symbol,这几个装箱类型(Boxed Types) 同样包含了一些超出预期的类型。
以 String 为例,它同样包括 undefined、null、void,以及代表的 拆箱类型(Unboxed Types) —> string,但并不包括其他装箱类型对应的拆箱类型,如 boolean 与 基本对象类型,我们看以下的代码:

const tmp9: String = undefined;
const tmp10: String = null;
const tmp11: String = void 0;
const tmp12: String = 'aaa';// !!!  以下不成立,因为不是字符串类型的拆箱类型
const tmp13: String = 599; 
const tmp14: String = { name: 'aaa' }; 
const tmp15: String = () => {}; 
const tmp16: String = []; 

object
object 的引入就是为了解决对 Object 类型的错误使用,它代表所有非原始类型的类型,即数组、对象与函数类型这些:

const tmp17: object = undefined;
const tmp18: object = null;
const tmp19: object = void 0;const tmp20: object = 'aaa';  // X 不成立,值为原始类型
const tmp21: object = 599; // X 不成立,值为原始类型const tmp22: object = { name: 'aaa' };
const tmp23: object = () => {};
const tmp24: object = [];

{ }
可以认为使用{ }作为类型签名就是一个合法的,但内部无属性定义的空对象,
这类似于 Object(想想 new Object()),它意味着任何非 null / undefined 的值:

const tmp25: {} = undefined; // 仅在关闭 strictNullChecks 时成立,下同
const tmp26: {} = null;
const tmp27: {} = void 0; // void 0 等价于 undefinedconst tmp28: {} = 'aaa';
const tmp29: {} = 599;
const tmp30: {} = { name: 'aaa' };
const tmp31: {} = () => {};
const tmp32: {} = [];

虽然能够将其作为变量的类型,但你实际上无法对这个变量进行任何赋值操作:

const tmp30: {} = { name: 'aaa' };tmp30.age = 18; // X 类型“{}”上不存在属性“age”。

总结

  • 在任何时候都不要,不要,不要使用 Object 以及类似的装箱类型。

  • 当你不确定某个变量的具体类型,但能确定它不是原始类型,可以使用
    object。但更推荐进一步区分,也就是使用
    Record<string, unknown> 或 Record<string, any> 表示对象,
    unknown[] 或 any[] 表示数组,
    (…args: any[]) => any表示函数 这样。

  • 我们同样要避免使用{}。{}意味着任何非 null / undefined 的值,从这个层面上看,使用它和使用 any 一样恶劣。

字面量类型和枚举

字面量类型

看一个例子,我们开发中定义的请求 接口返回 结果可能如下

interface Res {code: 10000 | 10001 | 50000;status: "success" | "failure";data: any;
}

上面"success" 或者 “failure” 不是一个值吗?为什么它也可以作为类型?

在 TypeScript 中,这叫做字面量类型(Literal Types),它代表着比原始类型更精确的类型,同时也是原始类型的子类型

字面量类型主要包括:字符串字面量类型、数字字面量类型、布尔字面量类型和对象字面量类型,它们可以直接作为类型标注:

const str: "aaa" = "aaa";
const num: 599 = 599;
const bool: true = true
// 报错!Type '"aaa123"' is not assignable to type '"aaa"'
const str1: "aaa" = "aaa123";

单独使用字面量类型比较少见,因为单个字面量类型并没有什么实际意义。
它通常和联合类型(即这里的 |)一起使用,表达一组字面量类型:

interface Tmp {bool: true | false;num: 1 | 2 | 3;str: "aa" | "bb" | "cc"
}

补充:联合类型

联合类型你可以理解为,它代表了一组类型的可用集合,只要最终赋值的类型属于联合类型的成员之一,就可以认为符合这个联合类型。
联合类型对其成员并没有任何限制,除了上面这样对同一类型字面量的联合,我们还可以将各种类型混合到一起:

interface Tmp {mixed: true | string | 599 | {} | (() => {}) | (1 | 2)
}

联合类型的常用场景之一是通过多个对象类型的联合,来实现手动的互斥属性,即这一属性如果有字段1,那就没有字段2:

interface Tmp {user:| {vip: true;expires: string;}| {vip: false;promotion: string;};
}declare var tmp: Tmp;if (tmp.user.vip) {console.log(tmp.user.expires);
}
枚举
// js中定义一些常量
export const PageUrl = {Home_Page_Url: "url1",Setting_Page_Url: "url2",Share_Page_Url: "url3",
}

使用ts的枚举 enum

enum PageUrl {Home_Page_Url = "url1",Setting_Page_Url = "url2",Share_Page_Url = "url3",
}
// 使用
const home = PageUrl.Home_Page_Url;

如果你没有声明枚举的值,它会默认使用数字枚举,并且从 0 开始,以 1 递增:

enum Items {Foo,Bar,Baz
}
// Items.Foo , Items.Bar , Items.Baz的值依次是 0,1,2 

如果你只为某一个成员指定了枚举值,那么之前未赋值成员仍然会使用从 0 递增的方式,之后的成员则会开始从枚举值递增。

enum Items {Foo,  // 0 Bar = 599,Baz // 600
}

枚举和对象的重要差异在于,对象是单向映射的,我们只能从键映射到键值。
而枚举是双向映射的,即你可以从枚举成员映射到枚举值,也可以从枚举值映射到枚举成员:

enum Items {Foo,Bar,Baz
}const fooValue = Items.Foo; // 0
const fooKey = Items[0]; // "Foo"

函数和class中的类型

函数
// 函数声明
function foo(name: string): number {return name.length;
}
// 函数表达式const foo = function (name: string): number {return name.length
}
const foo: (name: string) => number = function (name) {return name.length
}// 箭头函数
// 方式一
const foo = (name: string): number => {return name.length
}// 方式二 代码的可读性会非常差,不推荐
const foo: (name: string) => number = (name) => {return name.length
}

要么直接在函数中进行参数和返回值的类型声明,要么使用类型别名将函数声明抽离出来,如下:

type FuncFoo = (name: string) => numberconst foo: FuncFoo = (name) => {return name.length
}

如果只是为了描述这个函数的类型结构,我们甚至可以使用 interface 来进行函数声明:

interface FuncFooStruct {(name: string): number
}

void 类型

// 没有调用 return 语句
function foo(): void { }// 调用了 return 语句,但没有返回值
function bar(): void {return;
}

可选参数与 rest 参数

使用 ? 描述一个可选参数
注意:可选参数必须位于必选参数之后

// 在函数逻辑中注入可选参数默认值
function foo1(name: string, age?: number): number {const inputAge = age || 18; // 或使用 age ?? 18return name.length + inputAge
}// 直接为可选参数声明默认值
function foo2(name: string, age: number = 18): number {const inputAge = age;return name.length + inputAge
}

对于 rest 参数的类型标注也比较简单,由于其实际上是一个数组,这里我们也应当使用数组类型进行标注:

function foo(arg1: string, ...rest: any[]) { }// 元组标注
function foo(arg1: string, ...rest: [number, boolean]) { }foo("aaa", 18, true)

重载
在某些逻辑较复杂的情况下,函数可能有多组入参类型和返回值类型:

function func(foo: number, bar?: boolean): string | number {if (bar) {return String(foo);} else {return foo * 599;}
}

要想实现与入参关联的返回值类型,我们可以使用 TypeScript 提供的函数重载签名(Overload Signature),将以上的例子使用重载改写:

function func(foo: number, bar: true): string;
function func(foo: number, bar?: false): number;
function func(foo: number, bar?: boolean): string | number {if (bar) {return String(foo);} else {return foo * 599;}
}const res1 = func(599); // number
const res2 = func(599, true); // string
const res3 = func(599, false); // number

异步函数

async function asyncFunc(): Promise<void> {}

对于异步函数(即标记为 async 的函数),其返回值必定为一个 Promise 类型,而 Promise 内部包含的类型则通过泛型的形式书写,即 Promise < T >

class

一个函数的主要结构即是参数、逻辑和返回值,对于逻辑的类型标注其实就是对普通代码的标注,所以我们只介绍了对参数以及返回值的类型标注。

而到了 Class 中其实也一样,它的主要结构只有构造函数、属性、方法和访问符(Accessor),我们也只需要关注这三个部分即可

构造函数

class Foo {prop: string;constructor(inputProp: string) {this.prop = inputProp;}print(addon: string): void {console.log(`${this.prop} and ${addon}`)}get propA(): string {return `${this.prop}+A`;}set propA(value: string) {this.prop = `${value}+A`}
}

!!setter 方法不允许进行返回值的类型标注,你可以理解为 setter 的返回值并不会被消费,它是一个只关注过程的函数。

修饰符
分别有 public / private / protected / readonly
readonly 属于操作性修饰符(就和 interface 中的 readonly 意义一致)
其它三个属于访问性修饰符

  • public:此类成员在类、类的实例、子类中都能被访问。
  • private:此类成员仅能在类的内部被访问。
  • protected:此类成员仅能在类与子类中被访问,你可以将类和类的实例当成两种概念,即一旦实例化完毕(出厂零件),那就和类(工厂)没关系了,即不允许再访问受保护的成员。
class Foo {private prop: string;constructor(inputProp: string) {this.prop = inputProp;}protected print(addon: string): void {console.log(`${this.prop} and ${addon}`)}public get propA(): string {return `${this.prop}+A`;}public set propA(value: string) {this.propA = `${value}+A`}
}

抽象类
抽象类是对类结构与方法的抽象,简单来说,一个抽象类描述了一个类中应当有哪些成员(属性、方法等),一个抽象方法描述了这一方法在实际实现中的结构。

abstract class AbsFoo {abstract absProp: string;abstract get absGetter(): string;abstract absMethod(name: string): string
}

抽象类中的成员也需要使用 abstract 关键字才能被视为抽象类成员,如这里的抽象方法。我们可以实现(implements)一个抽象类:

class Foo implements AbsFoo {absProp: string = "aaa"get absGetter() {return "aaa"}absMethod(name: string) {return name}
}

另外使用 interface 不仅可以声明函数结构,也可以声明类的结构:

interface FooStruct {absProp: string;get absGetter(): string;absMethod(input: string): string
}class Foo implements FooStruct {absProp: string = "aaa"get absGetter() {return "aaa"}absMethod(name: string) {return name}
}

any、unknown、never

any 类型的主要意义,其实就是为了表示一个无拘无束的“任意类型”,它能兼容所有类型,也能够被所有类型兼容。

千万不要 anyscript !!!

unknown 类型的变量可以再次赋值为任意其它类型,但只能赋值给 any 与 unknown 类型的变量

let unknownVar: unknown = "aaa";unknownVar = false;
unknownVar = "aaa";
unknownVar = {site: "bbb"
};unknownVar = () => { }const val1: string = unknownVar; // Error
const val2: number = unknownVar; // Error
const val3: () => {} = unknownVar; // Error
const val4: {} = unknownVar; // Errorconst val5: any = unknownVar;
const val6: unknown = unknownVar;

any 放弃了所有的类型检查,而 unknown 并没有

let unknownVar: unknown;unknownVar.foo(); // 报错:对象类型为 unknown
// 类型断言
let unknownVar: unknown;(unknownVar as { foo: () => {} }).foo();

类型断言: 虽然这是一个未知的类型,但我跟你保证它在这里就是这个类型!

never 类型被称为 Bottom Type,是整个类型系统层级中最底层的类型

never 类型不携带任何的类型信息,因此会在联合类型中被直接移除
在这里插入图片描述

泛型

可以理解为一个接受参数的函数

类型别名type中的泛型

type Factory<T> = T | number | string;

type中的泛型大多是用来进行工具类型封装,比如

// 把一个对象中的所有属性变成 string类型
type Stringify<T> = {[K in keyof T]: string;
};// 复制对象中的所有属性类型
type Clone<T> = {[K in keyof T]: T[K];
};

看一个例子

type Partial<T> = {[P in keyof T]?: T[P];
};
interface IFoo {prop1: string;prop2: number;prop3: boolean;prop4: () => void;
}type PartialIFoo = Partial<IFoo>;// 等价于
interface PartialIFoo {prop1?: string;prop2?: number;prop3?: boolean;prop4?: () => void;
}

泛型还可以作为条件类型中的判断条件

type IsEqual<T> = T extends true ? 1 : 2;type A = IsEqual<true>; // 1
type B = IsEqual<false>; // 2
type C = IsEqual<'linbudu'>; // 2

可以设置默认值

  • List item
type Factory<T = boolean> = T | number | string;// 调用时就可以不带任何参数了,默认会使用我们声明的默认值来填充
const foo: Factory = false;

可以使用 extends 关键字来约束传入的泛型参数必须符合要求
比如:

  • A extends B 意味着 A 是 B 的子类型
  • ‘aaa’ extends string,18 extends number 成立。
  • 联合类型子集均为联合类型的子类型,即 1、 1 | 2 是 1 | 2 | 3 | 4 的子类型
  • { name: string } 是 {} 的子类型,因为在 {} 的基础上增加了额外的类型

看下面例子,根据传入的请求码判断请求是否成功

type ResStatus<ResCode extends number> = ResCode extends 10000 | 10001 | 10002? 'success': 'failure';type Res1 = ResStatus<10000>; // "success"
type Res2 = ResStatus<20000>; // "failure"type Res3 = ResStatus<'10000'>; // 类型“string”不满足约束“number”。
type ResStatus<ResCode extends number = 10000> = ResCode extends 10000 | 10001 | 10002? 'success': 'failure';type Res4 = ResStatus; // "success"

多泛型关联
不仅可以同时传入多个泛型参数,还可以让这几个泛型参数之间也存在联系,类似于传入多个参数

type Conditional<Type, Condition, TruthyResult, FalsyResult> =Type extends Condition ? TruthyResult : FalsyResult;//  "passed!"
type Result1 = Conditional<'aaa', string, 'passed!', 'rejected!'>;// "rejected!"
type Result2 = Conditional<'bbb', boolean, 'passed!', 'rejected!'>;

接口interface中的泛型

常见的一个例子应该还是响应类型结构的泛型处理:

interface IRes<TData = unknown> {code: number;error?: string;data: TData;
}

这个接口描述了一个通用的响应类型结构,预留出了实际响应数据的泛型坑位,然后在你的请求函数中就可以传入特定的响应类型了:

interface IUserProfileRes {name: string;homepage: string;avatar: string;
}function fetchUserProfile(): Promise<IRes<IUserProfileRes>> {}type StatusSucceed = boolean;
function handleOperation(): Promise<IRes<StatusSucceed>> {}

函数中的泛型

函数中的泛型是很常用的,主要是做:类型的自动提取

function handle<T>(input: T): T {}

我们为函数声明了一个泛型参数 T,并将参数的类型与返回值类型指向这个泛型参数。这样,在这个函数接收到参数时,T 会自动地被填充为这个参数的类型。

例子:

function handle<T>(input: T): T {}const author = "aaa"; // 使用 const 声明,被推导为 "aaa"let authorAge = 18; // 使用 let 声明,被推导为 numberhandle(author); // 填充为字面量类型 "aaa"
handle(authorAge); // 填充为基础类型 number

在基于参数类型进行填充泛型时,其类型信息会被推断到尽可能精确的程度,如这里会推导到字面量类型而不是基础类型。
这是因为在直接传入一个值时,这个值是不会再被修改的,因此可以推导到最精确的程度。而如果你使用一个变量作为参数,那么只会使用这个变量标注的类型(在没有标注时,会使用推导出的类型)。

例子:

function swap<T, U>([start, end]: [T, U]): [U, T] {return [end, start];
}const swapped1 = swap(["aaa", 18]); // const swapped1: [number, string]
const swapped2 = swap([null, 18]); // const swapped2: [number, null]
const swapped3 = swap([{ name: "aaa" }, {}]); // const swapped3: [{}, { name: string;}]

函数中的泛型同样存在约束与默认值

// 不再处理对象类型的情况了
function handle<T extends string | number>(input: T): T {}
// 只想处理数字元组的情况
function swap<T extends number, U extends number>([start, end]: [T, U]): [U, T] {return [end, start];
}

函数的泛型参数也会被内部的逻辑消费,如:

function handle<T>(payload: T): Promise<[T]> {return new Promise<[T]>((res, rej) => {res([payload]);});

对于箭头函数的泛型,其书写方式是这样的:

const handle = <T>(input: T): T => {}

在 tsx 文件中泛型的尖括号可能会造成报错,编译器无法识别这是一个组件还是一个泛型,此时你可以让它长得更像泛型一些:

const handle = <T extends any>(input: T): T => {}

需要注意的是,不要为了用泛型而用泛型,就像这样:
没有意义

function handle<T>(arg: T): void {console.log(arg);
};

Class 中的泛型

Class 中的泛型和函数中的泛型非常类似,只不过函数中泛型参数的消费方是参数和返回值类型
Class 中的泛型消费方则是属性、方法、乃至装饰器等。
同时 Class 内的方法还可以再声明自己独有的泛型参数。我们直接来看完整的示例:

class Queue<TElementType> {private _list: TElementType[];constructor(initial: TElementType[]) {this._list = initial;}// 入队一个队列泛型子类型的元素enqueue<TType extends TElementType>(ele: TType): TElementType[] {this._list.push(ele);return this._list;}// 入队一个任意类型元素(无需为队列泛型子类型)enqueueWithUnknownType<TType>(element: TType): (TElementType | TType)[] {return [...this._list, element];}// 出队dequeue(): TElementType[] {this._list.shift();return this._list;}
}
http://www.tj-hxxt.cn/news/17515.html

相关文章:

  • 怎么做网站建设11月将现新冠感染高峰
  • 一家只做家纺的网站怎么seo关键词优化排名
  • 拐角型网站百度关键词排名神器
  • 邯郸网站设计培训机构网站品牌推广策略
  • 做佣金单网站二级域名在线扫描
  • 网站管理后台下载企业自助建站
  • 张家港安监站网址网推什么平台好用
  • 做游戏网站用什么系统做廊坊seo排名
  • 网站开发合肥免费建网站最新视频教程
  • 龙岩网络图书馆注册刷移动端seo软件
  • 孝感网站建设xgsh成都seo经理
  • 国外网站兼职做效果图品牌营销公司
  • 能发朋友圈的网站建设语百度seo点击排名优化
  • 用台式机做网站服务器网站制作公司
  • 什么学做网站企业宣传方式
  • 哪个网站的课件做的好百度老年搜索
  • 西安做网站费用人员优化方案怎么写
  • 烟台市住房和规划建设管理局网站惠州网站建设方案推广
  • 河海大学学风建设网站广州关于进一步优化疫情防控措施
  • 兼职做页面的网站最近的新闻有哪些
  • 公众号推文制作网站沈阳网站制作优化推广
  • 如何用天地图做网站百度竞价返点一般多少
  • 网站空间和云主机seo信息是什么
  • 网页设计网站怎么放到域名里郑州seo优化哪家好
  • 专业网站建设价格大全怎么推广自己的店铺
  • 网站开发难不难东莞网络推广培训
  • 山东外贸国际网站建设如何进行搜索引擎优化 简答案
  • seo网站设计阿里云免费域名
  • 无锡网站开发平台百度云盘登录电脑版
  • 怎么自己做网站赚钱目前在哪个平台做推广好