typescript-函数/类/接口

本文详细介绍了TypeScript中的接口(Interface)类型,包括对象类型接口、可索引类型接口、接口定义函数和混合类型接口。接着讨论了函数的定义,如可选参数、默认参数、剩余参数和函数重载。然后,文章深入探讨了类的实现、继承、修饰符以及抽象类的概念,并阐述了`this`的关键作用。最后,讲解了类与接口的关系,包括类如何实现接口,接口如何继承,以及接口继承类的情况。

接口类型

对象类型接口


假设我要从后端获取一组数据,渲染到页面之中,我们可以这样定义:

interface List {
  id:number;
  name:string;
}

interface Result {
    data:List[]
}

function render (result:Result) {
  result.data.forEach((value) => {
    console.log(value.id,value.name)
  })
}

let result = {
  data : [
    {"id":1,"name":"A"},
    {"id":2,"name":"B"},
  ]
}

render(reslut)

这样就定义了一个接口类型——规范
但是ts使用了duck type

// 如果data是这样也不回报错
let result = {
  data : [
    {"id":1,"name":"A", sex:1},
    {"id":2,"name":"B"},
  ]
}

render(result)

由于采用了duck type 也就是鸭子类型,输入的参数只要“长的像鸭子,那么它就是鸭子”——只需要满足interface必要条件, 就ok

但是如果直接传值,不声明变量:就无法通过接口类型检查

// 这样是无法通过编译的
render({
  data : [
    {"id":1,"name":"A", sex:1},
    {"id":2,"name":"B"},
  ]
}
)

那如果想解决?出了赋值给一个变量进行类型断言
还可以通过as关键字进行断言

// 加上as断言,就可以绕过类型检查
render({
  data : [
    {"id":1,"name":"A", sex:1},
    {"id":2,"name":"B"},
  ]
} as Result
)
// 这种也行,但是不推荐,react中会产生歧义
render(<Result>{
  data : [
    {"id":1,"name":"A", sex:1},
    {"id":2,"name":"B"},
  ]
}
)

还可以使用索引签名:

interface List {
  readonly id:number; // id 只读
  name:string;
  age?:int;  // 该参数表示可有可无
  [x:string]:any
}

不确定一个接口中有多少属性:使用可索引类型的接口

既可以使用数字,也可以使用字符串

// 用任意的数字去索引stringarray,都会得到一个string, 相当于声明了一个字符串类型的数组
interface StringArray {
    [index: number]: string
}
let char:StringArray = ["A", "B"]

// 用字符串去索引一个接口
//  用任意的字符串去索引Names, 得到的结果都是string
interface Names {
    [x:string]:string; //  这样声明之后,就不能声明number类型的成员
    // y:number  这样是不被允许的
    [z: number]:string // 这样我们既可以用数字,也可以用字符串索引Names
    // 需要注意的是:数字索引的返回值,一定要是字符串类型索引的子类型,因为js会进行类型转换,将number转换为string,这样会保持类型的兼容性
    // [z:number]:number   // 这样就和string不兼容了, 如果要兼容,可以将 [x:string]:string 改为  [x:string]:any
}

接口定义函数

我们可以用一个变量定义函数

let add:(x:number,y:number) => number

我们可以用接口定义它 => 等价变量定义

interface Add {
    (x:number,y:number):number
}

我们还可以使用类型别名定义

type Add = {x:number,ty:number} => number
let add:Add = (x,y) => a + b

混合类型接口

解释:既可以定义一个函数,也可以像对象一样拥有属性和方法

interface Lib {
    ():void;  // 首先定义一个函数,假设没有返回值和参数
	version:string;
	doSomething(): viod;
}

定义好了接口,我们如何进行实现?

let lib:Lib = (() => {}) as Lib  // 进行断言
lib.version = '1.0'
lib.doSomething = () => {}

上面这样定义的lib属于暴露全局的,且是单列,如果像创建多个,可以进行‘域’限定:函数封装~

function getLib {
    let lib:Lib = (() => {}) as Lib  // 进行断言
	lib.version = '1.0'
	lib.doSomething = () => {}
    return lib
}
lib1 = getLib();
lib1.doSomething();
lib2 = getLib();
lib3 = getLib();
lib4 = getLib();

函数

函数的定义

方法1
通过关键字 function

function add(x:number, y:number){
  return x + y
}

方法2
通过一个变量来定义一个函数类型

let add:(x:number,y:number) => number

方法3
通过类型别名type定义一个函数类型

type add=(x:number,y:number) => number

方法4
通过interface来定义一个函数

interface add {
  (x:number,y:number):number
}

ts的参数需要一一对应,

可选参数

? 表示该参数是可选参数

function add(x:number,y?:number) {
// do sth
}

注意:可选参数必须位于必选参数之后!~ 类似于python的关键字参数必须在位置参数之后

默认参数

类似python里的关键字参数

function add(x:number,y = 0, z:number,q = 1) {
    return x + y + z + q
}

add(1, undefined,3) // 5

必选参数之前的默认参数是必须要传值的,在必选参数之后的默认参数是不传的

剩余参数 (…)

和es6一样

function add(x:number,...rest:number[]){
  return x + rest.reduce((pre,cur) => pre + cur )
}

add(1,2,3,4,5) // 15

函数重载

实现一个方法,如果参数都是数字,就累加,如果参数都是字符串,就连接
首先ts和其他语言不同,需要先声明多个函数对象,在实现重载

function add(...ret:number[]):number;
function add(...ret:string[]):string;
function add(...ret:any[]):any{
  let first = ret[0]
  if (typeof first == 'string') {
    return ret.join('')
  }
  if (typeof first == 'number') {
    return ret.reduce((pre,cur) => pre + cur )
  }
}

由于重载是按顺序查询函数列表,应该把最容易出现的数据类型的函数,写在最前面

实现一个类

ts中引入了class关键字,覆盖了es6中的类,也增加了一些特性。
先实现一个类:

class Dog {
  constructor (name:string) {
  // constructor 的返回值是Dog 类型,也就是实例本身this
    this.name = name
  }
  name:string
  run() {}
}

这里要注意:
ts里类的属性都是实例属性,而不是原型属性(prototype
ts里类成员方法都是实例方法
实例的属性!必须有初始化的值

类的继承——extends

其实更像是拓展的意思

class Husky extends Dog {
  constructor (name:string,color:string ) {
    super(name)
    this.color = color // this 必须在super之后调用
  }
  color:string
}

类的继承,会提示我们“派生类的构造函数必须包含super调用”,super代表父类的实例

ts的修饰符——对es6拓展

默认所有属性都有public声明,也可以显式声明
private声明的属性,只能被类本身调用,不能被子类和实例调用
如果给构造函数constructor使用private声明,那么表示这个类,不能被继承,也不能被实例化
protected:受保护成员,一个受保护成员,只能被类或者子类调用,而不能被实例调用,如果给构造函数constructor添加保护,那该类不能被实例化,只能被继承
readonly:只读属性,不多哔哩吧啦,一定要初始化,和实例属性是一样的
static:这种属性,只能通过类名来调用,不能通过实例调用,子类也可以调用

此外,还可以给构造函数的参数添加修饰符,使其成为实例属性 ,代码会更简洁一些,就不用在外头再声明了

抽象类:abstract

es中没有抽象类,ts对es进行了拓展,引入了抽象类:只能被继承,而不能被实例化的类,实现的方法可被子类使用,定义的抽象方法,子类必须实现(明确知道子类自己会实现,就没必要在父类进行实现了)
举例:

abstract  class Animal {
 eat (){
   // do sth
 }
 abstract  sleep():void
 
 
}

class Dog extends Animal {
  constructor (name:string) {
    super()
    this.name = name
  }
  name:string
  run() {}
  sleep () {
  // do sth 
  }
}
  • 抽象共性,提取代码共性,提高复用性
  • 用于实现多态:抽象方法在子类中不同的实现——同时多态也是这种oop的核心

this

对于返回了this的方法,可以实现链式调用~
这其实很好理解,就是方法返回了实例
在js里很常见
可以运用都不同的编程语言中
同样,this同样表现为多态

类和接口的关系:implements

在有些语言里,是没有interface类型的,只能通过class进行表现
看代码:一个接口可以约束类成员有哪些属性

interface Human {
  name:string;
  eat():void
}
// 用类来实现了接口
// 必须实现接口中声明的所有的属性
// class也可以增加自己的属性
// 接口只能约束类的公有成员
// 接口不能约束构造函数 
class Asian implements Human {
  constructor(name:string) {
    this.name = name;
  }
  name:string
  eat(){}
  sleep(){}
}

接口继承:extends

接口可像class 一样被继承,且可继承多个接口
以上代码还有效的

interface Man extends Human {
  run():void;
}

interface Child {
  cry():void;
}

interface Boy extend Man,Child {
 
}

let boy:Boy={ 
  name:"",
  run(){},
  eat(){},
  cry(){}
}

可以看出接口可以拆分出可以重用的接口
也可合并为一个接口

接口继承类

接口除了可以继承接口
还可以继承类:相当于 接口把类的成员都抽象类出来,只有类的成员结构,而没有具体的实现

class Auto {
  state = 1
  // private state2 = 0
}

interface AutoInterface extends Auto {
 // 啥也不写,相当于这个接口中隐含了state属性
}

class C implements AutoInterface {
  state = 1
}
// 子类
class Bus extends Auto implements AutoInterface {
  // 这里不需要实现state属性,因为是Auto的子类
}

接口在抽离类成员的时候,不仅仅抽离类公共成员,而且抽离了私有成员和受保护成员

总结interface和 class

接口和类

总结

  • 用类来实现了接口
  • 必须实现接口中声明的所有的属性
  • class也可以增加自己的属性
  • 接口只能约束类的公有成员
  • 接口不能约束构造函数

后续泛型还会在看到,ts的灵活性

import globals from "globals"; import pluginJs from "@eslint/js"; import tseslint from "typescript-eslint"; import pluginReact from "eslint-plugin-react"; /** @type {import('eslint').Linter.Config[]} */ export default [ { files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"] }, { languageOptions: { globals: { ...globals.browser, ...globals.node } } }, pluginJs.configs.recommended, ...tseslint.configs.recommended, pluginReact.configs.flat.recommended, { rules: { // eslint (http://eslint.cn/docs/rules) "no-var": "error", // 要求使用 let 或 const 而不是 var "no-multiple-empty-lines": ["error", { max: 1 }], // 不允许多个空行 "no-use-before-define": "off", // 禁止在 函数//变量 定义之前使用它们 "prefer-const": "off", // 此规则旨在标记使用 let 关键字声明但在初始分配后从未重新分配的变量,要求使用 const "no-irregular-whitespace": "off", // 禁止不规则的空白 "no-unexpected-multiline": "error", // 禁止空余的多行 "no-useless-escape": "off", // 禁止不必要的转义字符 // react "react/prop-types": "off", // 使用 TypeScript 或其他型检查工具,可以关闭此规则,因为可能已经有更好的型检查机制。 "react/react-in-jsx-scope": "off", // 如果你使用 React 17 及以上,不需要在每个文件中导入 React,可关闭此规则。 "react/display-name": "warn", // 对组件缺少 displayName 属性发出警告,有助于调试和性能分析。 // typeScript (https://typescript-eslint.io/rules) "@typescript-eslint/no-unused-vars": "error", // 禁止定义未使用的变量 "@typescript-eslint/prefer-ts-expect-error": "error", // 禁止使用 @ts-ignore "@typescript-eslint/no-inferrable-types": "off", // 可以轻松推断的显式型可能会增加不必要的冗长 "@typescript-eslint/no-namespace": "off", // 禁止使用自定义 TypeScript 模块和命名空间。 "@typescript-eslint/no-explicit-any": "off", // 禁止使用 any 型 "@typescript-eslint/ban-types": "off", // 禁止使用特定型 "@typescript-eslint/explicit-function-return-type": "off", // 不允许对初始化为数字、字符串或布尔值的变量或参数进行显式型声明 "@typescript-eslint/no-var-requires": "off", // 不允许在 import 语句中使用 require 语句 "@typescript-eslint/no-empty-function": "off", // 禁止空函数 "@typescript-eslint/no-use-before-define": "off", // 禁止在变量定义之前使用它们 "@typescript-eslint/ban-ts-comment": "off", // 禁止 @ts-<directive> 使用注释或要求在指令后进行描述 "@typescript-eslint/no-non-null-assertion": "off", // 不允许使用后缀运算符的非空断言(!) "@typescript-eslint/explicit-module-boundary-types": "off", // 要求导出函数的公共方法的显式返回和参数型 } } ]; 代码优化扩展,添加注释
最新发布
05-30
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值