1、基础类型

let a: (number | String) = new String('123')
// String 可以是 new String/ '' 形式, string 则不行

2、数组

数组特殊需求

数组可能是number、string 类型
数组中有一个元素可有可无
数组中前面固定, 后面可以随意添加

// 数组可能是number、string 类型
let arr: Array<number|string> = []
arr = ['123',1,2,3]

// 数字类型的数组
let arr1: number[] = []

// 数组中有一个元素可有可无
let arr2: [number, string?] = [1] // 元组

// 数组中前面固定, 后面可以随意添加
let arr3: [number, string?, ...(string|number)[]] = [1,'',123]

3、对象

基本使用

// 基本使用
let obj: {} = { a: 123, c: 123 } // 这样使用和 any 差不多意思,意义不是很大
// 一般使用
let obj: { x: 123, y: number} = { x: 123, y: 123}

对象特殊需求

规定键名是一个数字
属性可有可无
固定某几个属性, 其他属性随便添加

// 规定键名是一个数字
let obj1: { [key: number]: number } = { 1: 1 }

// 属性可有可无
let obj: { x: 123, y: number, z?: number} = { x: 123, y: 123}

// 固定某几个属性, 其他属性随便添加
let obj1: { x: 123, y: number, [key: number]: number|{} } = { x: 123, y: 1, 1: {}}

4、 函数

基本使用

// 直接定义一个函数的参数和返回
function f1(a:string): string {}
// 定义一个函数变量
let f2:(a:string)=>string = function(a){}

函数特殊需求

参数默认值和可选
this 处理
剩余参数处理
根据不同类型参数,有不同返回

// 参数和可选
function f1(a: number, b?: number) {}
// 剩余参数可选
function f1(a: number, b?: number, ...arg: (number|string)[]):any {}
// 参数默认值
function f1(a = 3) {}
// this 处理
function f3(this:void, a:number) {}
// 根据不同类型参数,有不同返回(重载)

5、类

class Jsclass {
  public a: number = 123
  static target: number = 123
  readonly c = 2
  f2(a: string): string

  f2(a: string) {
    return a
  }

  private f1() {
    console.log(1)
  }
}
// Jsclass  可以当作类型使用
let obj: Jsclass = { 
  a: 1, 
  c: 2, 
  f2: function(a) { return a }
}

6、特殊类型与高级类型

unknown 和 any

  1. any 和 unknow 类型都可以分配任何值
  2. Unkown 相当于更安全的 any, unknown 类型只能拿赋值给 unknown 或者 any
let value: unknown = {}
let value2: any = '123'

function f1(a:any):any {

}

let value3: string = value2 // any可以任意复制,会有问题
let vlaue4: string = vlaue // 这里报错, unknown 只能复制给 any/unknown 

never

  1. never 代表永远不会出现
  2. never 类型的变量可以赋值给任何东西, 但是 never 类型不能有任何值
  3. 错误类型,或者永远没有结果的返回值可以用 never
function throwError():never { throw new Error() }

联合类型

let a: string|number

交叉类型

class obj1class { a: number }
class obj2class { b: number }
let obj3: obj1class & obj2class  { a: 1, b: 2 }

7、接口与type


声明合并: 声明两个一样的接口名则会合并

type

  1. type 关键字用于给一个类型一个命名,可以用于各种类型的定义
  2. 常用于基础类型和联合,交叉类型
type aString = string | number | number[]
type obj1 = { a: number }
type obj2 = { b: string }
//继承(合并)
type objAll = obj1 & obj2
// 函数
type f1 = (a: string) => string

let b: aString = [1,2,3]

interface

  1. interface 意思为接口, 区别于type, 这是定义了一个可继承的接口
  2. 常用于类,对象的定义
// 函数、对象、 类、 数组
// 对象接口
interface ojb3 {
  type: { a1: number }
  type2?: string,
  [propName:string]: any
}

// 函数
function f2(a:obj3) {
  console.log(a.type.a1)
}
f2({type:{a1:23}})

interface fn {
  (a:number, b:number): string
}
let ff5: fn = function(a,b) {
  return ''
}

// 对象
interface obj5 {
  a: 123,
  f1: (a:number) => number
}
let obj: obj5 = { a:123, f1: (a: number) => {return a} }

// 类 - implement
interface person {
  age: number,
  eat(food: string): string
}

class Mary implements person {
  age: number
  eat(food: string): string

  constructor(age: number) {
    this.age = age
  }

  eat(food: string) {
    return "123"
  }
}

// 数组
interface arr{
  [propname: number]: string
}

let arr1:arr = ['1','123']

// 继承
interface interObj {
  num: number
}
interface interObj2 {
  str: string
}
interface interObj3 extends interObj,interObj2 {
  arr: number[]
}
let obj: interObj3  = {
  num:1,
  str:"1",
  arr: [1,2]
}

8. 泛型和断言

泛型 就是不知道是啥类型,在像C#和Java这样的语言中,可以使用泛型来创建可重用的组件,一个组件可以支持多种类型的数据。 这样用户就可以以自己的数据类型来使用组件。

泛型之Hello World

不用泛型的话,这个函数可能是下面这样:

function identity(arg: number|any): number|any {
    return arg;
}

使用any类型会导致这个函数可以接收任何类型的arg参数,这样就丢失了一些信息:传入的类型与返回的类型应该是相同的。如果我们传入一个数字,我们只知道任何类型的值都有可能被返回。

因此,我们需要一种方法使返回值的类型与传入参数的类型是相同的。 这里,我们使用了 类型变量,它是一种特殊的变量,只用于表示类型而不是值。

function identity<T>(arg: T): T {
    return arg;
}

我们定义了泛型函数后,可以用两种方法使用。 第一种是,传入所有的参数,包含类型参数:

let output = identity<string>("myString");  // type of output will be 'string'

这里我们明确的指定了T是string类型,并做为一个参数传给函数,使用了<>括起来而不是()。

第二种方法更普遍。利用了类型推论 -- 即编译器会根据传入的参数自动地帮助我们确定T的类型:

let output = identity("myString");  // type of output will be 'string'

使用泛型变量

使用泛型创建像identity这样的泛型函数时,编译器要求你在函数体必须正确的使用这个通用的类型。 换句话说,你必须把这些参数当做是任意或所有类型。

看下之前identity例子:

function identity<T>(arg: T): T {
    return arg;
}

如果我们想同时打印出arg的长度。 我们很可能会这样做:

function loggingIdentity<T>(arg: T): T {
    console.log(arg.length);  // Error: T doesn't have .length
    return arg;
}

如果这么做,编译器会报错说我们使用了arg的.length属性,但是没有地方指明arg具有这个属性。 记住,这些类型变量代表的是任意类型,所以使用这个函数的人可能传入的是个数字,而数字是没有 .length属性的。
现在假设我们想操作T类型的数组而不直接是T。由于我们操作的是数组,所以.length属性是应该存在的。 我们可以像创建其它数组一样创建这个数组:

function loggingIdentity<T>(arg: T[]): T[] {
    console.log(arg.length);  // Array has a .length, so no more error
    return arg;
}

我们也可以这样实现上面的例子:

function loggingIdentity<T>(arg: Array<T>): Array<T> {
    console.log(arg.length);  // Array has a .length, so no more error
    return arg;
}

泛型类型

我们创建了identity通用函数,可以适用于不同的类型。 我们研究一下函数本身的类型,以及如何创建泛型接口。

泛型函数的类型与非泛型函数的类型没什么不同,只是有一个类型参数在最前面,像函数声明一样

function identity<T>(arg: T): T {
    return arg;
}

let myIdentity: <T>(arg: T) => T = identity;

我们也可以使用不同的泛型参数名,只要在数量上和使用方式上能对应上就可以。

function identity<T>(arg: T): T {
    return arg;
}

let myIdentity: <U>(arg: U) => U = identity;

我们还可以使用带有调用签名的对象字面量来定义泛型函数:

function identity<T>(arg: T): T {
    return arg;
}

let myIdentity: {<T>(arg: T): T} = identity;

这引导我们去写第一个泛型接口了。 我们把上面例子里的对象字面量拿出来做为一个接口:

interface GenericIdentityFn {
    <T>(arg: T): T;
}

function identity<T>(arg: T): T {
    return arg;
}

let myIdentity: GenericIdentityFn = identity;

一个相似的例子,我们可能想把泛型参数当作整个接口的一个参数。 这样我们就能清楚的知道使用的具体是哪个泛型类型(比如: Dictionary而不只是Dictionary)。 这样接口里的其它成员也能知道这个参数的类型了。

interface GenericIdentityFn<T> {
    (arg: T): T;
}

function identity<T>(arg: T): T {
    return arg;
}

let myIdentity: GenericIdentityFn<number> = identity;

多个泛型

function fn<T, U>(a:T, b:U): T&U {
  return Object.assign(a,b)
}

默认值

function find<T=number>(arr:T[],index: number):Array<T> {
  return [arr[index]]
}

find([1,2, '12'], 1)

泛型约束

你应该会记得之前的一个例子,我们有时候想操作某类型的一组值,并且我们知道这组值具有什么样的属性。 在 loggingIdentity例子中,我们想访问arg的length属性,但是编译器并不能证明每种类型都有length属性,所以就报错了。

function loggingIdentity<T>(arg: T): T {
    console.log(arg.length);  // Error: T doesn't have .length
    return arg;
}

相比于操作any所有类型,我们想要限制函数去处理任意带有.length属性的所有类型。 只要传入的类型有这个属性,我们就允许,就是说至少包含这一属性。 为此,我们需要列出对于T的约束要求。

为此,我们定义一个接口来描述约束条件。 创建一个包含 .length属性的接口,使用这个接口和extends关键字来实现约束:

interface Lengthwise {
    length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
    console.log(arg.length);  // Now we know it has a .length property, so no more error
    return arg;
}

现在这个泛型函数被定义了约束,因此它不再是适用于任意类型:

loggingIdentity(3);  // Error, number doesn't have a .length property

我们需要传入符合约束类型的值,必须包含必须的属性:

loggingIdentity({length: 10, value: 3});

断言

我们Js是灵活的,而ts是严格类型的, 这导致他们可能会存在冲突。我们可以用sa断言来解决这个冲突。

interface obj1 {
  a:number,
  b:number
}

let obj:obj1 = {} as obj1
// 异步请求后
obj.res = 123

function f1(a:number[]|string) {
  let _a = a as Array<number>
  _a.forEach(item => {console.log(item)})
}


interface fn {
  (a: number): string
  a: number
}

let f2:fn = function(a:number) {return 123;} as fn
f2.a = 123
// 断言的另一种写法,等同上面
let f2:fn = <fn>function(a:number) {return 123;}
posted on 2023-04-30 20:45  京鸿一瞥  阅读(37)  评论(0编辑  收藏  举报