TS 的脚步已经拦不住,代码撸起来

等级 671 0 0

前言

vue3已经发布了,ts的脚步已经阻拦不住了,还只会es6?别想了,人家都已经在行动了,以下是ts的基本系列教程,ts的基本语法,高级语法等,以及在vue项目中如何应用ts,跟着我赶紧撸起来吧。

基本数据类型

数字
const a: number = 3;  
字符串
const b: string = "1";  
数组
const c: number[] = [1, 2, 3];  
const d: Array<number> = [1, 3];  
const arr: any[] = [1, "33", true];  
元组

可以为数组中的每个参数定义相对应的类型

const e: [number, string] = [1, "ww"];  
枚举
enum error {  
  blue = 3,  
  "orange",  
}  
const f: error = error.orange;  
console.log(f); //输出4  

tips

  1. 如果未赋值的上一个值是数字那么这个未赋值的值的是上一个值的值+1

  2. 如果未赋值上一个值未赋值那么输出的就是它的下标

  3. 如果未赋值的上一个值的值是非数字,那么必须赋值

布尔类型
const g: boolean = true;  
对象

const i: object = {};  
undefined

常用于组合类型

let j: number | undefined;  
null
let k: null;  
void

指定方法类型,表示没有返回值,方法体中不能return

function aa(): void {  
  console.log(1);  
}  

//如果方法有返回值,可以加上返回值的类型  
function bb(): number {  
  return 1;  
}  
never

其他类型 (包括null和undefined)的子类型,代表从不会出现的值

let l: never;  

//匿名函数并抛出异常  
l = (() => {  
  throw new Error("111");  
})();  
任意类型

让参数可以是任何一种类型


let h: any = 1;  
h = true;  
h = "st";  

函数

函数申明
function cc(): void {}  
方法传参
function getUserInfo(name: string, age?: number, school: string = "清华大学") {  
  return `name:${name}--age:${age}--school:${school}`;  
}  

tips: ?代表这个参数可传可不传,不传就是undefined,也可定义个默认的值

剩余参数

传递多个时,如果用了剩余参数,就可以把未定义的形参转换为数组。

function sum (a: number, b: number, ...arr: number[]): number {  
  let sum: number = a + b;  
  arr.forEach((element) => {  
    sum += element;  
  });  
  console.log(arr); [3,4,5]    
  return sum;  
}  
console.log(sum(1, 2, 3, 4, 5)); //15  
函数重载
function reload(name: string): string;  
function reload(age: number): string;  
function reload(param: any): any {  
  return typeof param === "string" ? `我是:${param}` : `我的年龄:${param}`;  
}  
console.log(reload(18)); //年龄  

tips: 被重载的方法,是没有方法体,可以根据参数的类型走其中一个方法并判断参数,但如果传入的参数类型不是任何被重载方法的参数类型就不允许通过。

 第 1 个重载(共 2 个),“(name: string): string”,出现以下错误。  
   类型“never[]”的参数不能赋给类型“string”的参数。  
 第 2 个重载(共 2 个),“(age: number): string”,出现以下错误。  
   类型“never[]”的参数不能赋给类型“number”的参数


class Person {  
  // 私有变量  
  private name: string;  

  // 构造函数  
  constructor(name: string) {  
    this.name = name;  
  }  

  // 获取名字  
  getName(): string {  
    return this.name;  
  }  

  // 设置名字  
  setName(name: string): void  {  
    this.name = name;  
  }  
}  

let p = new Person("张三");  
p.setName("李四");  
console.log(p);  

继承

class Son extends Person {  
 // 静态属性  
  public static age: number = 18;  
  // 学校  
  public school: string;  
  //构造方法  
  constructor(name: string, school: string) {  
    // 访问派生类的构造函数中的 "this" 前,必须调用 "super",初始化父类构造函数 --并把参数传给父类  
    super(name);   
    //把传进来的school赋值给全局变量  
    this.school = school;  
  }  
  //静态方法  
  static run(name: string): string {  
    return `${name}在跑步,他的年龄才${this.age}`;  
  }  
}  

let son = new Son("王五", "清华大学");  
son.setName("赵六"); // 私有类也不能在子类的外部访问,但可通过公开的方法中进行赋值和访问  
console.log(son);  
console.log(Son.run("方七"));  
console.log(Son.age);  

tips:

  1. public 在当前类里面,子类,类外面都可以访问

  2. protected 在当前类和子类内部可以访问,类外部无法访问

  3. private 在当前类内部可访问,子类,类外部都无法访问。

  4. 属性不加修饰符,默认就是公有的 (public)

多态

通过抽象方法/方法重载--实现多态--多态的作用是用来定义标准

// 抽象父类  
abstract class Animal {  
  private name: string;  
  constructor(name: string) {  
    this.name = name;  
  }  
  //抽象成员--方法  
  abstract eat(): any;  
  //抽象成员--属性  
  protected abstract ages: Number;  
  sleep(): void {  
    console.log("睡觉");  
  }  
}  

class cat extends Animal {  
  ages: Number = 2;  
  constructor(name: string) {  
    super(name);  
  }  
  //非抽象类“cat”不会自动实现继承自“Animal”类的抽象成员“eat”,  必须手动定义父类中的抽象方法--多态  
  eat(): string {  
    return "猫吃鱼";  
  }  

  //多态  
  sleep(): string {  
    return "猫在睡觉";  
  }  
}  

console.log(new cat("33").sleep());  

tips:

  1. 抽象类无法实例化

  2. 非抽象类继承抽象父类时不会自动实现来自父类的抽象成员,必须手动定义父类中的抽象成员,否则报错。

  3. 抽象成员包括属性方法

接口

在面向对象的编程中,接口是一种规范的定义,它定义了行为和动作的规范,

在程序设计里面,接口起到一种限制和规范的作用。

接口定义了某一批类所需要遵守的规范,接口不关心这些类的内部状态数据,也不关心这些类里方法的实现细节,它只规定这批类里必须提供某些方法,提供这些方法的类就可以满足实际需要。ts中的接口类似于java,同时还增加了更灵活的接口类型,包括属性、函数、可索引和类等。

属性接口
interface InterfaceName {  
  first: string;  
  second?: string; //加个问号,接口属性就可以变成可传可不传了,不传默认是undefined。  
}  
//打印变量  
function logParam(name: InterfaceName): void {  
  console.log(name.first, name.second, 11);  
}  
//定义参数  
const obj = { first: "1", second: "fff", three: 1 };  
//logParam({ first: "1", second: "1", three: 1 }); //报错,只能传接口定义的值  
logParam(obj);  

tips: 用个变量来存储传入的变量,这样可以传入定义的接口以外的值,否则如果直接传入对象中无接口定义的值会报错,所以建议接口定义了哪些值就传哪些值。

函数类型接口

对方法传入的参数类型,以及返回值类型进行约束,可批量进行约束。

interface keyMap {  
  (key: string, value: string): string;  
}  
let logKeyMap: keyMap = function (key1: string, value: string): string {  
  return key1 + value;  
};  
console.log(logKeyMap("key1", "value"));  

tips: 接口只对传入的参数的类型和参数的个数进行约束,不对参数名称进行约束。

可索引接口
  • 约束数组
interface Arr {  
  [index: number]: string;  
}  
let ss: Arr = ["2121"];  
  • 约束对象
interface Obj {  
  [index: string]: string;  
}  

let interfaceArr: Obj = { aa: "1" };  

tips:

  1. 数组进行约束,index后必须跟着number类型。

  2. 对象进行约束,index后必须跟着string类型

  3. 索引签名参数类型必须为 "string" 或 "number"

类类型接口
  • 进行约束,类似抽象类的实现。
interface Animals {  
  name: string;  
  eat(): void;  
}  

class Dogs implements Animals {  
  name: string;  
  constructor(name: string) {  
    this.name = name;  
  }  
  eat() {}  
}  
  • 接口继承--接口可以继承接口
interface Dog {  
  eat(): void;  
}  

interface Persons extends Dog {  
  work(): void;  
}  

class Cat {  
  code() {  
    console.log("猫在敲代码");  
  }  
}  

//可继承类后再实现接口  
class SuperMan extends Cat implements Persons {  
  eat(): void {  
    console.log(1);  
  }  
  work(): void {  
    console.log(2);  
  }  
}  
let superMan = new SuperMan();  
superMan.code();  

tips: 类接口会对类的属性方法进行约束,类似非抽象类继承抽象类时必须实现某些方法和属性,但对属性和方法的类型的约束更加严格,除了方法void类型可被重新定义外,其他属性或方法的类型定义需要和接口保持一致。

泛型

软件工程中,我们不仅要创建一致的定义良好的api,同时也要考虑可重用性。
组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能

泛型就是解决接口方法复用性,以及对不特定数据类型的支持。

要求:传入的参数和返回的参数一致

函数的泛型
function getDate<T>(value: T): T {  
  return value;  
}  
console.log(getDate<number>(123));  

tips: 这里的T可改成其他任意值但定义的值,和传入的参数以及返回的参数是一样的,一般默认写法是T,也是业内规范的选择。

类的泛型
class MinClass<T> {  
  public list: T[] = [];  
  //添加  
  add(value: T): void {  
    this.list.push(value);  
  }  

  //求最小值  
  min(): T {  
    //假设这个值最小  
    let minNum = this.list[0];  
    for (let i = 0; i < this.list.length; i++) {  
    //比较并获取最小值  
    minNum = minNum < this.list[i] ? minNum : this.list[i];  
    }  
    return minNum;  
  }  
}  
//实例化类 并且指定了类的T的类型是number  
let minClass = new MinClass<number>();   
minClass.add(23);  
minClass.add(5);  
minClass.add(2);  
console.log(minClass.min());  
 //实例化类 并且指定了类的T的类型是string,则其方法的传参和返回都是string类型  
let minClass2 = new MinClass<string>();  
minClass2.add("23");  
minClass2.add("5");  
minClass2.add("2");  
console.log(minClass2.min());  
接口的泛型
  • 第一种写法
interface ConfigFn {  
  //规范参数类型,返回值类型  
  <T>(value: T): T;  
}  

let getData: ConfigFn = function <T>(value: T): T {  
  return value;  
};  

console.log(getData<string>("z11"));  
  • 第二种写法

interface ConfigFn<T> {  
  //参数类型 ,返回值类型  
  (value: T): T;  
}  

//接口方法  
function getData<T>(value: T): T {  
  return value;  
}  

//使用接口  
let myGetDate: ConfigFn<string> = getData;  
console.log(myGetDate("3"));  

tips:接口的泛型只针对函数类型的接口

类当做参数传入泛型类
//用户类--和数据库表字段进行映射  
class User {  
  username: string | undefined;  
  password: string | undefined;  
  //构造函数-初始化参数  
  constructor(param: {  
    username: string | undefined;  
    password?: string | undefined;  
  }) {  
    this.username = param.username;  
    this.password = param.password;  
  }  
}  


//数据库类  
class Db<T> {  
  add(user: T): boolean {  
    console.log(user);  
    return true;  
  }  
  updated(user: T, id: number): boolean {  
    console.log(user, id);  
    return true;  
  }  
}  

let u = new User({  
  username: "张三",  
});  

//u.username = "李四";  
u.password = "111111";  
let db = new Db<User>();  
db.add(u);  
db.updated(u, 1);  

tips: 类的参数名和类型都做了约束。

模块

内部模块称为命名空间,外部模块简称为模块,模块在其自身的作用域里执行,而不是在全局作用域里;

这意味着定义在一个模块里的变量、函数、类等等在模块外部是不可见的,除非你明确的使用export形式之一导出它们。

相反,如果想使用其它模块导出的变量,函数,类,接口等的时候,你必须要导人它们,可以使用import形式之一。

我们可以一些公共的功能单独抽离成一个文件作为一个模块。
模块里面的变量、函数、类等默认是私有的,如果我们要在外部访问模块里面的数据(变量、函数、类)
我们需要通过export暴露模块里面的数据(变量、函数、类...)。
暴露后我们通过import引入模块就可以使用模块里面暴露的数据(变量、函数、类...)

//modules/db.ts  
function getDate(): any[] {  
  console.log("获取数据");  
  return [  
    {  
      userName: "张三",  
    },  

    {  
      userName: "李四",  
    },  
  ];  
}  

//一个模块里面可以用多次  
// export { getDate };  
//一个模块里面只能用一次  
export default getDate;  
 import { getDate as getDbDate } from "./modules/db";  
 import getDbDate from "./modules/db";  
 getDbDate();

tips: 这个调试时浏览器中不能直接使用,可在nodeweakpack的环境中调试。

命名空间

在代码量较大的情况下,为了避免各种变量命名相冲突,可将相似功能的函数、类、接口等放置到命名空间内
TypeScript的命名空间可以将代码包裹起来,只对外暴露需要在外部访问的对象。

命名空间和模块的区别

  • 命名空间:内部模块,主要用于组织代码,避免命名冲突。

  • 模块:ts外部模块的简称,侧重代码的复用,一个模块里可能会有多个命名空间。

 // modules/Animal.ts  
export namespace A {  
  interface Animal {  
    name: String;  
    eat(): void;  
  }  

  export class Dog implements Animal {  
    name: String;  
    constructor(theName: string) {  
      this.name = theName;  
    }  
    eat() {  
      console.log("我是" + this.name);  
    }  
  }  
}  

export namespace B {  
  interface Animal {  
    name: String;  
    eat(): void;  
  }  

  export class Dog implements Animal {  
    name: String;  
    constructor(theName: string) {  
      this.name = theName;  
    }  
    eat() {}  
  }  
}
 import { A, B } from "./modules/Animal";  
 let ee = new A.Dog("小贝");  
 ee.eat();

装饰器

  • 类装饰器:类装饰器在类申明之前被申明(紧靠着类申明),类装饰器应用于类构造函数,可以用于监视,修改或者替换类定义。
function logClass(params: any) {  
  console.log(params);  
  //params 就是指代当前类--HttpClient  
  params.prototype.apiUrl = "动态扩展属性";  
  params.prototype.run = function () {  
    console.log("动态扩展方法");  
  };  
  params.prototype.getDate = function () {  
    console.log("动态扩展方法2");  
  };  
}  

@logClass  
class HttpClient {  
  constructor() {}  
  getDate() {  
    console.log(1);  
  }  
}  

let http: any = new HttpClient();  
console.log(http.apiUrl);  
http.run();  
http.getDate();  

tips: 装饰器会覆盖被装饰的类中的方法。

  • 装饰器工厂

可传参的装饰器


function logClassB(param: string) {  
  return function (target: any) {  
    console.log(target, "装饰器以下的类");  
    console.log(param, "装饰器传进来的属性");  
  };  
}  

@logClassB("小慧")  
class HttpClients {  
  constructor() {}  
  getDate() {}  
}  

let https: any = new HttpClients();  
console.log(https);  
  • 构造函数装饰器
function logClassC(target: any) {  
  console.log(target, 1111);  
  //用在这里继承目标类并重载方法和属性  
  return class extends target {  
    a: any = "我是修改后的属性";  
    getDate() {  
      console.log(this.a + "--装饰器中的方法输出的");  
    }  
  };  
}  

@logClassC  
class HttpClient2 {  
  public a: string | undefined;  
  constructor() {  
    this.a = "我是构造函数里面的a";  
  }  
  getDate() {  
    console.log(this.a);  
  }  
}  
const https2 = new HttpClient2();  
https2.getDate();  

来自:SegmentFault,作者:yaoxfly

链接:https://segmentfault.com/a/1190000031793196

收藏
评论区

相关推荐

TS 的脚步已经拦不住,代码撸起来
前言 vue3已经发布了,ts的脚步已经阻拦不住了,还只会es6?别想了,人家都已经在行动了,以下是ts的基本系列教程,ts的基本语法,高级语法等,以及在vue项目中如何应用ts,跟着我赶紧撸起来吧。 基本数据类型 数字 const a: number  3; 字符串 const b: string  "1
Dart中的泛型、泛型方法、泛型类、泛型接口
一、Dart中的泛型 泛型方法 通俗理解:泛型就是解决 类 接口 方法的复用性、以及对不特定数据类型的支持(类型校验) 一般用   T   表示泛型 getData<T(T value){ return
C# 泛型特化
C# 泛型不是 C++ 的模板类,并不支持特化和偏特化,但是使用一些技巧可以在一定程度上达到相同的目的。 原文是 po 在 stackoverflow 上的一个回答:[A: Generic indexer overload specialization](https://www.oschina.net/action/GoToLink?url=https%3
C#泛型
现在的netcore 3.1和最新的.netframework8早已经没有当初那个被人诟病的ArrayList了,但很巧这玩意不得不说,因为它决定了C#团队痛改前非,抛弃过往重新上路,上一段ArrayList案例代码。 public class ArrayList { private object[]
C#非泛型集合和泛型集合的超级详解(转)
C# 泛型集合之非泛型集合类与泛型集合类的对应: ------------------------ ArrayList对应List HashTable对应Dictionary Queue对应Queue Stack对应Stack SortedList对应SortedList ###  转自(https://www.cnblogs.com/cheng
Java scirpt 简介
### javascript简介:   JavaScript一种直译式脚本语言,是一种动态类型、弱类型、基于原型的语言,内置支持类型。它的解释器被称为JavaScript引擎,为浏览器的一部分,广泛用于客户端的脚本语言,最早是在HTML(标准通用标记语言下的一个应用)网页上使用,用来给HTML网页增加动态功能。 ### 基本特点:   JavaScri
Java 泛型
命名类型参数 推荐的命名约定是使用大写的单个字母名称作为类型参数。这与 C++ 约定有所不同(参阅 附录 A:与 C++ 模板的比较),并反映了大多数泛型类将具有少量类型参数的假定。对于常见的泛型模式,推荐的名称是: K —— 键,比如映射的键。 V —— 值,比如 List 和 Set 的内容,或者 Map 中的值。 E
Java泛型的使用
### 泛型的定义: 泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型。这个类型参数将在使用时(例如,继承或实现这个接口,用这个类型声明变量、创建对象时)确定(即传入实 际的类型参数,也称为类型实参)。 ### 泛型的引入背景: 集合容器类在设计阶段或声明阶段不能确定这个容器到底实际存储的是什么类型的对象
Java泛型详解
**引言** ------ Java泛型是jdk1.5中引入的一个新特性,泛型提供了编译时的类型检测机制,该机制允许程序员在编译时检测到非法的类型。 泛型是Java中一个非常重要的知识点,在Java集合类框架中泛型被广泛应用。本文我们将从零开始来看一下Java泛型的设计,将会涉及到通配符处理,以及让人苦恼的类型擦除。 **泛型基础** --------
Java的泛型详解(一)
Java的泛型详解(一) 编写的代码可以被不同类型的对象所重用。 因为上面的一个优点,泛型也可以减少代码的编写。 1|2泛型的使用 简单泛型类 public class Pair { private T first; private T second; public Pair() { first = null; second = null;
java 泛型详解
对java的泛型特性的了解仅限于表面的浅浅一层,直到在学习设计模式时发现有不了解的用法,才想起详细的记录一下。 本文参考java 泛型详解、Java中的泛型方法、 java泛型详解 1\. 概述 泛型在java中有很重要的地位,在面向对象编程及各种设计模式中有非常广泛的应用。 什么是泛型?为什么要使用泛型? 泛型,即“参数化类型”。一提到参数,最熟
java泛型
一、实现机制 java泛型实现方法为类型擦除,基于这种方法实现的泛型称为伪泛型。 java泛型只在源代码中存在,在编译后的文件中替换为原生类型,并插入强制转换。 (真正的泛型是应该存在于源码、编译后文件、运行期) 二、擦除实例 源码: List<String> testList = new ArrayList<String>();
Angular技巧汇总
一、声明全局的类型定义 -----------     声明项目的全局类型,同时不需要在各个Ts文件中import {XXX} from 'xxx'  ,就能直接引用!方法是:      增加src/typings.d.ts文件 ,在文件中增加  **interface IName {  name:string ; }** 的类型定义。      那么
Deno 继颠覆 Node 之后,又“内部”拒绝了 TypeScript
**![](https://oscimg.oschina.net/oscnet/6685b21a8f75772aea98ccda4f1a5745411.png)** Deno 团队计划删除所有内部代码构建时的 TS 类型检查与捆绑。打算将所有运行时代码转移到同一个 JavaScript 文件当中,但仍将使用随附的 d.ts 文件保存类型定义与说明文档。理由
TypeScript Generics(泛型)
软件工程的一个主要部分就是构建组件,构建的组件不仅需要具有明确的定义和统一的接口,同时也需要组件可复用。支持现有的数据类型和将来添加的数据类型的组件为大型软件系统的开发过程提供很好的灵活性。 在C#和Java中,可以使用"泛型"来创建可复用的组件,并且组件可支持多种数据类型。这样便可以让用户根据自己的数据类型来使用组件。 **泛型的简单案例** 首先,