TypeScript 入门学习笔记 + 练习题

在这里插入图片描述

安装

npm install -g typescript

检查版本号

tsc -v

实现保存自动编译

文件夹下输入 tsc --init 初始化,生成 tsconfig.json

outDir 配置项为修改编译 js 文件输入的目录

在这里插入图片描述
点击终端 -> 运行任务

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
就可以保存自动编译。

Tip:如果你是第一次配置,可能会和我一样,出现报错,禁止加载脚本,可以参考 这篇文章👈

1. 类型 (Number, String, Boolean, Array, Tuple, Enum, Any, Void,Never)

Boolean

let flag:boolean = true

number

let a:number = 123
a = 12.6 // 浮点类型是可以的,统称数字类型

String

let str:string = 'this is ts'

Array

// 1.(数组里面只能存放数字)
let arr:number[] = [1, 2, 4, 'ashga']
// (数组里面只能存放字符)
let arr:string[] = ['php', 'js', 'c', 2323]

// 如果出现了别的类型,TS 是会报错的
// 这样的写法也是可以的
let arr:Array<number> = [1, 2, 4]
let arr:Array<string> = ['jj', 'b', 'c']
// any 任意类型,这就和之前写法差不多了
let arr3:any[] = ['a', 1, 'o']

Tuple

// 元祖类型(tuple)可以指定数组元素的多种类型
let arr:[string, number, boolean] = ['ts', 3, true]

Enum

语法

enum 枚举名 {
  标识符[=整型常数],
  标识符[=整型常数],
  标识符[=整型常数],
  标识符[=整型常数]
}
enum Flag {
  success = 1,
  error = 0
}

let f:Flag = Flag.error
console.log(f)
enum Color {
  red,
  blue,
  orange
}

let c:Color = Color.orange
console.log(c)
// 未赋值输出的是索引值(和数组索引一致,都是由0开始起算的)

如果为其中的某一个赋值,那么位于该值下方的数据索引依次 + 1

let r:Color = Color.red
let b:Color = Color.blue
let o:Color = Color.orange
console.log(r, b, o)   // 0 5 6

Any

typescript 不推荐使用 any 类型,因为那样就失去了使用 typescript 的优势

let num:any = 123
num = 'str'
console.log(num)

null 和 undefined

null 和 undefined 其他(never 类型)数据类型的子类型

let num:number
console.log(num)  // 报错 undefined

let num:undefined
console.log(num)    // 正确 undefined

let num:number | undefined
num = 123
console.log(num)

// 定义没有赋值, 就是 undefined
let num:number | undefined
console.log(num)
let num:null
num = null
console.log(num)
// 一个元素可能是 number 类型 可能是 null, 可能是 undefined
let num:number | undefined | null
num = 1234
console.log(num)

Void

void 类型 ts中的void表示没有任何类型,一般用于定义方法的时候方法没有返回值

// 方法没有返回任何类型
function run():void{
    console.log('run')
}

// 指定方法类型
function run():number{
    return 1234
}
run()

Never

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

// 返回never的函数必须存在无法达到的终点
function error(message: string): never {
    throw new Error(message);
}

2. 函数的定义

函数声明法

// 代表返回值是一个 string 类型
function run():string {
  return 'run'
}

// 匿名函数
let fun2 = function():number {
  return 123
}
console.log(fun2())

ts 中定义方法传参

function getInfo(name:string, age:number):string {
  return `${name}---${age}`
}
console.log(getInfo('sdhgvs', 12))

let getInfo = function(name:string, age:number):string {
  return `${name}---${age}`
}

函数无返回值

function getInfo():void{
  console.log('hahahah')
}

可选参数

// 注意:可选参数必须配置到参数的最后面
function getInfo(name: string, age?: number): string {
  if (age) {
    return `${name}---${age}`;
  } else {
    return `${name}---age is null`;
  }
}
console.log(getInfo("zs", 56));
console.log(getInfo('coco'));

默认参数

function getInfo(name: string, age: number = 68): string {
  if (age) {
    return `${name}---${age}`;
  } else {
    return `${name}---age is null`;
  }
}
console.log(getInfo("zs", 56));
console.log(getInfo('coco'));

剩余参数

function sum(...result: number[]): number {
  let sum = 0;
  for (let i = 0; i < result.length; i++) {
    sum += result[i];
  }
  return sum;
}

console.log(sum(1, 2, 3, 4, 6, 7, 8, 9, 10));
function sum(a: number, b: number, ...result: number[]): number {
  let sum = 0;
  for (let i = 0; i < result.length; i++) {
    sum += result[i];
  }
  console.log(a + b);
  return sum;
}

函数重载

java 中方法的重载,重载指的是两个或者两个以上同名函数,但是他们的参数不一样,这时会出现函数重载的情况。typescript 中的重载,通过为同一个函数提供多个函数类型定义来试下多种功能的目的。

function getInfo(name: string): string;

function getInfo(age: number): number;

function getInfo(str: any): any {
  if (typeof str === "string") {
    return "我叫" + str;
  } else {
    return "我的年龄" + str;
  }
}
console.log(getInfo("zs"));
console.log(getInfo(20));
function getInfo(name: string): string;

function getInfo(name: string, age: number): string;

function getInfo(name: any, age?: any): any {
  if (age) {
    return `我叫${name},我的年龄是${age}`;
  } else {
    return `我叫${name}`;
  }
}
console.log(getInfo("zs"));
console.log(getInfo("zs", 25));

3.

类的定义

class Person {
  name:string; // 属性
  // 构造函数,实例化类的时候触发的方法
  constructor(name:string) {
    this.name = name;
  }
  run():void {
    console.log(this.name + ' is running')
  }
}

let zs = new Person('zs')
zs.run() // zs is running
console.log(zs) // Person { name: 'zs' }
class Person {
  name:string;
  constructor(name:string) {
    this.name = name;
  }
  getName():string {
    return this.name
  }

  setName(name: string):void {
    this.name = name
  }
}

let zs = new Person('zs')
console.log(zs.getName())

zs.setName('lisi')
console.log(zs.getName())

继承

class Person {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
  run(): string {
    return `${this.name}在运动`;
  }
}

class Web extends Person {
  constructor(name: string) {
    super(name);
  }

  run(): string {
    return `${this.name}在运动-------`;
  }

  work(): void {
    console.log(`${this.name}在工作`);
  }
}

const w = new Web("哈哈哈");
console.log(w.run());
w.work();

类的修饰符

  • public:在类里面,子类,类外面都可以访问
  • protected:在类里面,子类里面可以访问,类外面没法访问
  • private:在类里面可以访问,子类,类外面无法访问
class Person {
  public name: string;
  protected age?: number;
  private sex?: number;

  constructor(name: string, age?: number, sex?: number) {
    this.name = name;
    this.age = age;
    this.sex = sex;
  }

  getPri(): string {
    return `性别:${this.sex}`;
  }

  run(): string {
    return `${this.name}在运动`;
  }
}

const p = new Person("hehe");
console.log(p.name); // hehe
console.log(p.sex) // 报错:属性“sex”为私有属性,只能在类“Person”中访问
class Web extends Person {
  constructor(name: string, age?: number, sex?: number) {
    super(name, age, sex);
  }

  work(): void {
    console.log(`${this.name}在工作`);
  }
  
  run(): string {
    return `${this.age}在运动------- `;
  }

  // 报错:属性“sex”为私有属性,只能在类“Person”中访问。
  getPri(): string {
    return `性别:${this.sex}`;
  }
}

const w = new Web("哈哈哈", 35);
console.log(w.run());
w.work();
// 属性“age”受保护,只能在类“Person”及其子类中访问。
console.log(w.age)

静态属性和静态方法

class Person {
  static str: string = "CoCo";
  name: string;
  constructor(name: string) {
    this.name = name;
  }
  // 静态方法没法直接调用类里面的属性,只能调用静态属性
  static print(): void {
    console.log(`static function name ${this.str}`);
    // 类型“typeof Person”上不存在属性“name”。
    // console.log(`static function name ${this.name}`);
  }
}

const p = new Person('lily');
Person.print();

多态

父类定义一个方法不去实现,让继承它的子类去实现,每一个子类有不同的表现。多态属于继承。

class Animal {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
  eat(): void {
    console.log(`吃的方法`);
  }
}

class Dog extends Animal {
  constructor(name: string) {
    super(name);
  }
  eat(): string {
    return this.name + "吃骨头";
  }
}

class Cat extends Animal {
  constructor(name: string) {
    super(name);
  }
  eat(): string {
    return this.name + "吃老鼠";
  }
}

抽象类

  • Typescript 中的抽象类,它是提供其他类继承的基类,不能直接被实例化
  • 用 abstract 关键字定义抽象类和抽象方法
  • 抽象类中的抽象方法不包含具体实现,并且必须在派生类中实现
  • abstract 抽象方法只能放在抽象类里面
abstract class Aminal {
  public name: string;
  constructor(name: string) {
    this.name = name;
  }
  abstract eat(): any;
}

抽象类和抽象方法用来定义标椎类,这个类要求它的子类必须包含父类中定义的方法

class Dog extends Aminal {
  constructor(name: string) {
    super(name);
  }
  eat(): any {
    console.log(`${this.name} 在吃饭`);
  }
}

let dog = new Dog('仔仔');
dog.eat();

4. 接口

作用:在面向对象的编程中,接口是一种规范的定义,它定义了行为和动作的规范,在程序设计里面,接口起到了一种限制和规范的作用,接口定义了某一批所需遵守的规范,接口不关心这些类的内部状态数据,也不关心这些类里方法的实现细节,它只规定这批内里必须提供某些方法,提供这些方法的类就可以满足实际需要,typescript中的接口类似于java,同时还增加了更灵活的接口类型,包括属性,函数,可索引和类等

就像现实生活中,你要使用这个接口,就必须遵循它的规范

在这里插入图片描述

对自定义方法传入参数进行约束

function printLabel(labelInfo: { label: string }): void {
  console.log(labelInfo);
}

// 错误
// printLabel('hahah')
// printLabel({name: 'zs'})

// 正确
// 传入的参数必须是 label 开头的
printLabel({ label: "typescript" });

对批量方法传入参数进行约束,interface 定义接口

interface FullName {
  firstName: string;
  secondName: string;
}

function printName(name: FullName) {
  // 必须传入对象 firstName secondName
  console.log(name.firstName + ":" + name.secondName);
}

// 错误
// printName('章', '鱼哥')

printName({
  firstName: "章",
  secondName: "鱼哥",
});

严格按照接口的规范进行传参

interface FullName {
  firstName: string;
  secondName: string;
}

function printInfo(info: FullName) {
  // 类型“FullName”上不存在属性“age”
  console.log(info.firstName + info.secondName + info.age)
}

let info = { age: 14, firstName: 'll', secondName: 'mimimi' }
printInfo(info)

可选属性

interface FullName {
  firstName: string;
  secondName?: string;
}

function getName(name: FullName) {
  console.log(name);
}

getName({
  firstName: "仔仔",
  // secondName: 'hahah',
});

小案例:使用 typescript 对 ajax 进行封装

interface Config {
  type: string;
  url: string;
  data?: string;
  dataType: string;
}

function ajax(config: Config) {
  let xhr = new XMLHttpRequest();
  xhr.open(config.type, config.url, true);
  xhr.send(config.data);
  xhr.onreadystatechange = function () {
    if (xhr.readyState === 4 && xhr.status === 200) {
      if (config.dataType === "json") {
        console.log(JSON.parse(xhr.responseText));
      }
    }
  };
}

ajax({
  type: "get",
  data: 'name',
  url: "http://a.itying.com/api/productlist",
  dataType: "json",
});

函数类型接口

对方法传入的参数以及返回值进行约束

interface encrypt {
  (key: string, value: string): string;
}

// 函数在定义的时候,必须遵循 encrypt 接口的规范
let md5:encrypt = function(key: string, value: string): string {
  return key + value
}

console.log(md5('md5', 'hash'))

可索引接口,数组

interface UserArr {
  [index: number]: string;
}
let arr: UserArr = ["a", "b"];
console.log(arr[0]);

let arr1: UserArr = [123, "bbb"]; // 错误
console.log(arr1[0]);

可索引接口,对象的约束

// 
interface UserObj {
  [index: string]: string;
}

// 数组的索引是 number 类型
let arr: UserObj = ['1'];

let ary: UserObj = { name: "20" };

类类型接口:对类的约束和抽象类有点相似

interface Animal {
  name: string;
  eat(str: string): void;
}

class Cat implements Animal {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
  eat(): void {
    console.log(`${this.name} is good`);
  }
}

var d = new Cat("小黑");
d.eat();

class Fish implements Animal {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
  eat(food: string): void {
    console.log(`${this.name}${food}`);
  }
}

var f = new Fish("旺财");
f.eat("吃骨头");

接口可以继承接口

// 接口扩展,接口可以继承接口

interface Animal {
  eat(): void;
}

interface Person extends Animal {
  work(): void;
}

class progrommer {
  public name: string;
  constructor(name: string) {
    this.name = name;
  }

  coding(code: string): void {
    console.log(`${this.name} ${code}`);
  }
}

// Web 类必须实现 eat 和 work 这两个方法
// Person 接口继承自 Animal 接口
class Web extends progrommer implements Person {
  constructor(name: string) {
    super(name);
  }

  eat(): void {
    console.log(`${this.name} 吃馒头`);
  }

  work(): void {
    console.log(`${this.name} police`);
  }
}

const w = new Web("kola");

w.work();
w.eat();
w.coding("ts code");

5. 泛型

软件工程中,我们不仅要创建一致的定义良好的API,同时也要考虑可重用性。 组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能。在像C#和Java这样的语言中,可以使用泛型来创建可重用的组件,一个组件可以支持多种类型的数据。这样用户就可以以自己的数据类型来使用组件。通俗理解,泛型就是解决接口方法的复用性以及对不特性数据类型的支持。

function getData(value: string): string {
  return value;
}

我们希望实现同时返回 string 类型 和 number 类型,传入什么,返回什么。支持不特定的数据类型。但不能使用 any,因为 any 会放弃类型检查。

// T 表示泛型,具体什么类型是调用这个方法的时候决定的
function getData<T>(value: T): T {
  return value; //传入什么返回什么
}
// 这样调用
getData<number>(123123)
getData<string>("字符串")

类的泛型

获取传入数值的最小值

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++) {
      if(minNum > this.list[i]) {
        minNum = this.list[i]
      }
    }
    return minNum
  }
}

let m = new MinClass<number>()
m.add(1)
m.add(2)
m.add(0)
console.log(m.min())

let s = new MinClass<string>()
s.add('t')
s.add('a')
s.add('b')
s.add('c')
console.log(s.min())

泛型接口

interface ConfigFn {
  <T>(value: T): T;
}

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

getData<string>('张三')
getData<number>(123)
interface ConfigFn<T> {
  (value: T): T;
}

function getData<T>(value: T): T {
  return value
}

let myGetDataString:ConfigFn<string> = getData
let myGetDataNumber:ConfigFn<number> = getData

myGetDataString('张三')
myGetDataNumber(123)

使用泛型对类的参数进行校验

class ArticleCate {
  title: string | undefined;
  desc: string | undefined;
  status?: number | undefined;
  constructor(param: {
    title: string | undefined;
    desc: string | undefined;
    status?: number | undefined;
  }) {
    this.title = param.title;
    this.desc = param.desc;
    this.status = param.status
  }
}

class MySqlDb<T>{
  add(info: T): boolean {
    console.log(info)
    return true
  }
  update(info: T, id: number):boolean {
    console.log(info)
    console.log(id)
    return true
  }
}

let article1 = new ArticleCate({
  title: '那个他',
  desc: '言情小说',
  status: 1
})

let db = new MySqlDb<ArticleCate>()

db.add(article1)

6. 模块化

你可以这样导出

let dbUrl:string = 'xxxxxx'

export {
  dbUrl
}

这样导出

export function getData():any[] {
  console.log('获取数据库的数据')
  return [
    {
      title: 'title'
    }
  ]
}

这样导入

import { dbUrl } from './modules/db'

对导入进行重命名

import { getData as get} from './modules/db'

每个模块都可以有一个 default 导出,默认导出使用 default 关键字标记,并且一个模块只能够有一个 default 导出。

function updateData():void {
  console.log('更新数据')
}

export default updateData

导入

import updateData from './modules/db'

7. 装饰器

class HttpClient {
  constructor() {
  }
  getData() {

  }
}

这是一个类,你希望在不修改当前类的情况下,为该类增加属性和方法

function logClass(params: any) {
  console.log(params) // parmas 就是当前的类
  params.prototype.apiUrl = '动态扩展的属性'
  params.prototype.run = function() {
    console.log('function run()')
  }
}

这是你希望增加的属性和方法,那我们可以在之前的类前,使用装饰器

@logClass
class HttpClient {
  constructor() {
  }
  getData() {

  }
}

测试:实例化

let http = new HttpClient()

console.log(http.apiUrl)
http.run()

对于传参问题

function logClass(params: string) {
  // params => 传入的参数
  // target => 当前的类
  return function(target: any) {
    console.log(params, target)
    target.prototype.apiUrl = params
  }
}

使用该装饰器的时候,就可以进行传参

@logClass('http://www.hello.com')
class HttpClient {
  // ....
}

测试

let http = new HttpClient()
console.log(http.apiUrl)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值