基础

let hello = "hello world";
console.log(hello);

ts 需要通过ts命令编译成对应的javaScript
tsc main.ts => 目录下会出现对应的main.js文件
node main.js
终端回出现打印的代码就成功了

TypeScript 的工作流程
Code.ts => TSC(Code.ts) => Code.js

  1. 给文件目录添加一个index.html文件
  2. 引入
  3. 这时候就可以打开html文件,浏览器中打开控制台,可以看到打印的 hello world
  4. 但是如果吧 let hello = "我来了",这时候浏览器是没办法直接更改的
  5. 这时候就需要进行 tsc main.ts 更新新的main.js文件
  6. 更新main.js 文件后刷新浏览器之后会控制台出现更新的内容 => 我来了
  7. 这时候发现,如果老是这样更新文件,会耗费大量的时间,所以这个时候就需要用到npm 来进行项目管理了
  8. 终端中直接 输入 npm init
  9. 出现对应的问题,如果是练习的话,直接一步到位,一路回车,或者瞎写就好
  10. 出现文件package.json => 这里是管理项目的依赖文件
  11. 现在直接给它安装几个依赖
  12. 终端中输入 => npm install --save-dev lite-server
  13. 终端运行完之后,会发现目录下多了=> node_modules文件,该文件为项目的所有依赖存放的地方
  14. 同时发现 package.json 代码里出现了 "devDependencies": {"lite-server": "^2.6.1"}
  15. 顺便说下这里的npm 的延伸知识 => 这里有两个管理项目的依赖,一个是 devDependencies,另一个是 dependencies,
  16. 两个的区别:devDependencies => 只参与项目的开发,开发完成以后,上线并不需要打包到生产环境中.不需要部署到服务器
  17. dependencies 这个则相反
  18. 在package.json 代码里 scripts 加上 "start":"lite-server"
  19. 这个lite-server会根据更目录的index.html 部署到本地的localhost去,这样部署的好处可以避免为们更改代码的时候刷新页面.
  20. 把 let hello = "hello world"; 改成 let hello = "hello world123"; 终端输入 tsc mian.ts 后,浏览器的控制台打印更改了

TypeScript 的基础类型

变量声明

在javaScript中,和TypeScript的变量声明都是 => const let var 三种
但是TypeScript 的官方明确说明了 任何情况下避免使用 var ,尽量用 const 或者 let (作用域问题)

列子
var number1 = 1;
let number2 = 2;
const number3 = 3;
//const 是不能修改常量  number3 = 3 是会报错不能修改的
let 是ES6标准下的 标识
let 跟 var 的区别
function doSomething() {
    // for (var i = 0; i < 0; i++) {
    //     console.log(i);
    // }
    // console.log("console i", i); //var 的时候 这里是能打印出 i 的循环值 0 1 2 3 4
    for (let i = 0; i < 0; i++) {
        console.log(i);
    }
    // console.log("console i", i); //let 的时候 这里就会找不到“i”
}
doSomething();

所以在开发中尽量是用let

TypeScript 类型简介

TypeScript 与 JavaScript 的最大区别在与 TypeScript 对类型的强定义 它给 JavaScript 带来了多种基本类型

JavaScript 的基本类型 : boolean string number array null undefinded object

TypeScript 的基本类型在 JavaScript 基础上多了 : tuple (元组) enum (枚举) void never any

除了基本类型, TypeScript 还给出了 => 高级类型 : union(组合类型) Nullable(可空类型) Literal(预定义类型)

number

对数字定义只有一个很笼统的 number 来表示
既能表示整数、也能表示浮点数.甚至也可以表示正负数
例如: 1、 5.3 、 -10
JavaScript 和 TypeScript没有太大的区别.

let number1 = 1;
let number2 = 2;
let number3 = 3;
function add(n1, n2) {
    return n1 + n2;
}
// add(number1, number2); // 3 number 类型
// ts  能在开发中更好的避免类型的错误导致出现错误
function add2(n1: number, n2: number) {
    return n1 + n2;
}
// add2(number1, number2); // 3 number 类型
// 或者对 参数本身定义类型
//let number1:number = 1
//let number2:number = 2

// String
// "hello"  'hello'  `hello`
// 反引号: `` ,可以创建一个字符串模版 包括分行,甚至填充变量
// JavaScript 和 TypeScript没有太大的区别.

let number4 = "5";
let number5 = 2;
function add3(n1, n2) {
    return n1 + n2;
}
// add3(number4, number5); // 52 string 类型  字符串中相加就是拼接
// ts  能在开发中更好的避免类型的错误导致出现错误
function add4(n1: string, n2: number) {
    return n1 + n2;
}
//let number4:string = '5'
// let number4:string = `5`

boolean

JavaScript 和 TypeScript 没有太大的区别.

let isTrue = true;

他会自动指向数据类型 => 鼠标移到 isTrue 上,可以看到 let isTrue : boolean
但是把 isTrue = "true" => 就会报错了,因为上面定义的时候 typeScript 就已经定义好 isTrue 就是 +boolean 的类型,而不是 string 类型
这就是自动映射类型, 如果不想自动映射 可以 let isTrue : boolean

数组(Array) 和 元组(Tupple)

Array

Array
数组很重要,在 javascript 中应用很广泛
数组 : []
数组中可以存放任意类型的数据
JavaScript 中数组的宽容度非常大,而且 TypeScript 也很好的继承了这一点

let list: number[] = [1, 2, 3];
let list1: Array<number> = [1, 2, 3];
let list2 = [1, 2, 3];
// 鼠标指向 list list1 list2 每个上,出现的数据类型都是 let XXX = number[]
// 注意的是原声的 JavaScript 的数组是一个混合类型,既可以存放数字也可以存放字符串,ts也很好的继承了这一点,
// 例如
let list3 = [1, "123"]; //这时候鼠标指向 list3 会出现 let list3:(string | number)[]
let list4: any[] = [1, "dss", true]; //这时候鼠标指向 list4 会出现 let list3:any[] ,这时候就是可以在数组中存放任何类型的数据
Tupple 元组类型
let person: [number, string] = [1, "元组"];

这时候就会觉得这不就是一个数组,哪是什么元组, 但是他是一个固定长度,固定类型的数组,鼠标指向 person 的时候会出现

let person:[number,string]

如果 person[0] = "123" ,person[1]= 123 就会报错了
甚至给数组添加一个元素 person[2]= 123 Type '123' is not assignable to type 'undefined' 长度是不可以改变的
所以说 Tupple 是一个固定长度,固定类型的特殊数组
这个是 固定长度,固定长度的数组有什么处呢,它很自然的形成了 key:value 的键值对了 ['key',1],在开发中更好的的逻辑判断,和数据处理

但是弊端也能很明显 要注意了两点

  1. 官方也没有解决的bug, person[2]= 123 是会报错的,但是 person.push(3) 是没有报错的 但是我们 person 的长度之定义了长度为 0,1 的,却能够push 2 的长度数据
  2. 在使用元组的时候一定要声明数据类型,

例如

let person1 = [1, "123"]; // let person1:(string | number)[] 的联合类型数组,而不是元组类型了 它既不是固定长度,固定类型的数组,是个混合类型的数组
//  person1[0] = "123"
//  person1[1] = 456 是没有报错的

联合 Union

let union: string | number;
union = 2;
union = "2";
// union = true 定义以外的会报错

let union1: string | number | boolean | string[];
function merge(n1: number, n2: number) {
    return n1 + n2;
}

function merge2(n1: number | string, n2: number | string) {
    if (typeof n1 === "string" || typeof n2 === "string") {
        return n1.toString() + n2.toString();
    } else {
        return n1 + n2;
    }
}
let mergeNumber = merge2(2, 5);
let mergeString = merge2("hello", "world"); //这里报错了,是因为merge不支持string的类型

// console.log(mergeNumber);
// console.log(mergeString);
// 终端输入 => tsc main.js  node main.js
// 7, helloworld

字面量 literal

const number6 = 5; //鼠标指向number6 看到的是 const number6:5 ,这就是字面量类型
// 几个类型的联合
let union2: 0 | 1 | 2;
union2 = 1;
// union2 = 4; 报错了

let literal: 1 | "2" | true | [1, 2, 3, 4];
// 这种方法能产生非常强大的代码逻辑
// 例如

function merge3(
    n1: number | string,
    n2: number | string,
    resultType: "as-number" | "as-string"
) {
    if (resultType === "as-string") {
        return n1.toString() + n2.toString();
    }
    if (typeof n1 === "string" || typeof n2 === "string") {
        return n1.toString() + n2.toString();
    } else {
        return n1 + n2;
    }
}
let mergeNumber1 = merge3(2, 5, "as-number");
let mergeNumber2 = merge3(2, 5, "as-string");
let mergeString1 = merge3("hello", "world", "as-string"); //这里报错了,是因为merge不支持string的类型

// console.log(mergeNumber1); // 7
// console.log(mergeNumber2); // 25
// console.log(mergeString1); // helloworld
// 终端输入 => tsc main.js  node main.js

枚举 (Enum) // JavaScript 和 TypeScript

这里有个比较特殊,在 JavaScript 中并没有 枚举(Enum) 的概念,在ES3中就作为关键词保留,但是在 JavaScript 中并没有实际的使用过,所以 TypeScript 就实现了 JavaScript 没有使用的宿愿,

enum Color {
    red,
    green,
    blue,
}

let color = Color.blue;

// console.log(color); // 2 枚举打印出来的是对应 Color 的下标,也可以在鼠标指向 枚举 Color 的 blue 上就能看到 (enum number) Color.blue = 2

// 也可以自己指定数据
enum Color1 {
    red = 5,
    green = 10,
    blue = 1,
}
// 这样的也没问题的

enum Color3 {
    red = "red",
    green = "green",
    blue = 1,
}
let color3 = Color3.green;
// console.log(color3); //green

// 总之 enum 在typeScript 中 配合 switch 语句非常强大

any (任意)

let randomValue: any = 666;
randomValue = true;
randomValue = "asdasd";
randomValue = {};
// randomValue(); //在这里不会报错但是,运行的时候就会报错了,因为它不是一个函数,而是 666 ,他自然不会被 JavaScript 执行
// randomValue.toUpperCase();

为什么 TypeScript 是给 JavaScript 带来强类型定义 ,还要去 设置一个 any 的任何类型的参数呢,一个是为了快速开发,不会笼长或者没有必要约定的熟成的类型定义.
JavaScript的灵活性,在any也体现出了 TypeScript 继承 JavaScript 的灵活性
但是 JavaScript的灵活性 带来了 不可阅读性 不可维护性 不可拓展性 等一系列问题,TypeScript 就是给 JavaScript 带来了强类型语言,形成了语言的高维护性
与any 非常相似的类型 就是 unknown

unknown

let randomValue1: unknown = 666;
randomValue1 = true;
randomValue1 = "asdasd";
randomValue1 = {};
// randomValue1(); //报错了
// randomValue1.toUpperCase(); //报错了
// 证明 unknown 不保证类型,却保证类型安全,在我们使用 unknown 类型时需要做一定程度判断,类型转换之后,确定变量类型才能正常使用
// 所以
if (typeof randomValue1 === "function") {
    randomValue1(); //没有报错
}

if (typeof randomValue1 === "string") {
    randomValue1.toUpperCase(); //没有报错
}

使用any 还是 unknown 结合自身或项目需要使用,但是 any 会留下一些安全隐患 , unknown 会比 any 保险一点,可以保证类型的安全

void

function pruntResult() {
    console.log("123");
}

鼠标指向 pruntResult 会看到 function pruntResult() :void 在一个函数没有任何返回的情况下就是一个 void 函数

在ts中我们也可以给函数指定了类型

function pruntResult1(): void {
    console.log("123");
    return;
}
// console.log(pruntResult1); //undefined
// 在 JavaScript 没有 void 的这个表述的

undefined

如果 把 void 改成 undefined

function pruntResult2(): undefined {
    console.log("123");
    return;
}
  1. 这时候就会报错 鼠标指向会看到 : 其声明类型不为 "void" 或者 "any" 的函数必须返回值
  2. 也就是说 undefined 也是一个值 ,是一个未初始化的值,使用void 就是不存在的东西,就是一直不存在的意思
  3. void 和 undefined 实际是一个哲学问题 在探讨某一个物质是否存在的问题
  4. 强调: 再原生 JavaScript 中,是没有 void 的类型,只有 undefined , 再 TypeScript 的所有 void 在 JavaScript 中输出都是返回 undefined

never

function throwError(message: string, errorCode: number) {
    throw {
        message,
        errorCode,
    };
}
throwError("not found", 404);
  1. 鼠标指向 throwError 的时候 会看到 => function throwError(message: string, errorCode: number):void
  2. 因为我们函数 throwError 抛出了(throw)异常,不仅没有返回值,而且它永远都不可能执行完成,因为执行到 throw 的时候的 3. 直接抛出异常,函数强行结束了,函数永远不可能执行下去,
  3. 所以这 throwError 函数正真的类型应该是 never
    如:

    function throwError1(message: string, errorCode: number): never {
     throw {
         message,
         errorCode,
     };
    }
    // throwError1("not found", 404);
    ``
    除了 throw 抛出异常的方法无法完成以外还有什么方法呢?
    > 例如 while 循环
    

    function whileLoop() {
    while (true) {

     console.log("123");

    }
    }
    // 所以
    function whileLoop1(): never {
    while (true) {

     console.log("123");

    }
    }

    
    ### 类型适配(类型断言) Type Assertions
    

    let message: any;
    message = "abc";

    这里正常情况写 message 应该变成了string 的类型 ,但是在我们使用 JavaScript 内嵌函数时
    message.endWith("c");
    这时候鼠标指向message 的时候,数据类型还是 let message :any 并不是 let message :string,因为初始化时any类型,就算后续赋值为字符串,初始化的类型还是不会改变
    所以 message 是不能使用 endWith 这个内嵌函数的
    应该 => 两种方法

    //方法一
    (message) = "abc";
    let ddd = (message).endsWith("c");
    console.log(ddd);

    //方法二
    let ddd2 = (message as string).endsWith("c");

    提示:但是在使用类型适配的时候,必须非常了解变量的类型,而且对自己的代码有100% 的信心,否则引发严重的错误
    
    ###  函数类型 // JavaScript 和 TypeScript
    

    let log = function (message) {
    // console.log(message);
    };

let log2 = (message: string) => {

// console.log(message);

};
// log2("123");

let log3 = (message: string, code: number) => {

console.log(message, code);

};
log3("hello", 2);

在 JavaScript 中 和 TypeScript 有个显著的不同,调用函数过程中,如果 TypeScript 定义了两个参数,必须填写参数重的所有参数,而且类型必须匹配一致

也有办法对方法的参数可选性, 后面加上 ?

let log4 = (message: string, code?: number) => {

console.log(message, code);

};

也可以给参数设置默认值

let log5 = (message: string, code: number = 0) => {

console.log(message, code);

};
// log5("hello"); //code 不传时候默认为 0 ,如果传递,即为传递的参数

 注意: => 不管是可选参数,还是默认参数,都必须在参数的末尾,而且从参数末尾往前的顺序进行排列

let log6 = (message?: string, code: number) => {

console.log(message, code);

}; // 这样是会报错


### Object

在 JavaScript 中 object 为 const obj = {hello:'world'},hello 为 PROPERTY VALUE , word 为 PROPERTY NAME(KEY) => 键值对 ,和JSON数据基本一样的 ,TypeScript 也一样

const persons = {

firstName: "马云",
lastName: "马",
age: 100,

};
console.log(persons);

鼠标指向persons 就是它自己
这里要注意了在 JavaScript 中对象是以键值对存在 => key:value,而  TypeScript 是 key to type 键类型对
可以手动定义类型

const persons1: {

firstName: string;
lastName: string;
age: number;

} = {

firstName: "马云",
lastName: "马",
age: 100,

};

但是笼统的定义 Object

const persons2: object = {

firstName: "马云",
lastName: "马",
age: 100,

};
// console.log(persons2.firstName);//会报错 鼠标指向会报错 类型 "object" 不存在属性 object


### interface

let drawPoint = (Point) => {
console.log({ x: Point.x, y: Point.y });
};
drawPoint({ x: 105, y: 24 });

这里不管是传数字,还是字符串,或者参数名都不一致

drawPoint({ x: "测试", y: "啊啊啊" });
drawPoint({ wether: "干燥", temperature: 24 });

这样就会造成不是我们想要的数据了
所以我们用到 interface

interface Point {
x: number;
y: number;
}

加入了接口, 但是又引发另一个问题,对象的聚合问题,就是 高内聚.低耦合,也就是功能相关的事物应该放在一个集合中形成一个模块,这个就叫高内聚,而这些模块有相对独立的,不同模块之间应该保持低耦合状态
计算两点之间的距离

let getDistances = (a: Point, b: Point) => {};


### class

1. 我们吧所有的方法都写成函数,或者独立的文件中,在团队开发中就会出现东一块,西一块的了.这样慢慢的就会变到越来越不可维护,所以要用到了面对对象的方法创建一个集合来处理,这个集合就是 class
2. https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Classes
3. 在 javascript 中 => 类是用于创建对象的模板。他们用代码封装数据以处理该数据。 JS中的类建立在原型上,但也具有某些语法和语义未与ES5类相似语义共享。
4. class 就是一组紧密相关的属性和函数的集合,

// 计算两点的距离

interface IPoint {

x: number;
y: number;
drawPoint: () => void;
getDistances: (p: IPoint) => number;

}

class Point implements IPoint {

x: number;
y: number;

constructor(x?: number, y?: number) {
    this.x = x;
    this.y = y;
}
drawPoint = () => {
    console.log("x:", this.x, "y:", this.y);
};
getDistances = (p: IPoint) => {
    return Math.pow(p.x - this.x, 2) + Math.pow(p.y - this.y, 2);
};

}

// const point = new Point();
// point.drawPoint();
// 对象object、类class、实例interface 的关系
// 对象object const point = new Point(); 使用new 实例化产生一个对象,也称为 实例interface,
// 这个时候 // 终端输入 => tsc main.js node main.js x 和 y 都是 undefined ,x和y没有初始化
//point.x = 2; point.y = 3; x,y赋值


1. 这时候还有更好的办法去初始化这个坐标的值,使用构造函数 constructor,唯一缺点使用构造函数的时候需要使用到构造函数的标识
2. 注意:=>  javascript 中的构造函数中有有一个明显的不同,javascript 的构造函数不可以重载 overload 也就是说一个类有且仅有一个 constructor ,
3. 所以给 constructor 的参数加上可选值,或者 默认值就很重要了
4. 所以class 可以生成一些模版代码,节省大量的劳动力

1. 在构造函数中 constructor 给参数传递参数,会变的很繁琐 在ts中在语法层面做好了优化,就很好的帮我们解决了这个问题,通过使用访问修饰符 在生成构造函数的同时顺便完成成员的变量声明以及初始化
2. 如在构造函数的参数中加上关键词 public、private、protected

```
interface IPoint2 {
    x: number;
    y: number;
    drawPoint: () => void;
    getDistances: (p: IPoint2) => number;
}

class Point2 implements IPoint2 {
    // x: number;
    // y: number;
    // 因为 x,y 都是公有属性 ,所以使用 public,当我们在构造函数中是有访问修饰符,修饰参数的同时,constructor 构造函数也会帮我们自动生成他们所对应的成员变量.
    constructor(public x: number, public y: number) {
        // constructor 构造函数也会帮我们成员变量赋值吗,但是这种时候就不能使用参数缺省的方法了 => x?: number, y?: number
        // this.x = x;
        // this.y = y;
    }
    drawPoint = () => {
        console.log("x:", this.x, "y:", this.y);
    };
    getDistances = (p: IPoint2) => {
        return Math.pow(p.x - this.x, 2) + Math.pow(p.y - this.y, 2);
    };
}

// const point2 = new Point2(2, 3);
// point2.drawPoint();

```

### Access Modifier 访问修饰符: public
```
interface IPoint3 {
    // x: number;
    // y: number;
    drawPoint: () => void;
    getDistances: (p: IPoint3) => number;
}

class Point3 implements IPoint3 {
    // x: number;
    // y: number;
    // 因为 x,y 都是公有属性 ,所以使用 public,当我们在构造函数中是有访问修饰符,修饰参数的同时,constructor 构造函数也会帮我们自动生成他们所对应的成员变量.
    constructor(private x: number, private y: number) {
        // constructor 构造函数也会帮我们成员变量赋值吗,但是这种时候就不能使用参数缺省的方法了 => x?: number, y?: number
        // this.x = x;
        // this.y = y;
    }
    drawPoint = () => {
        console.log("x:", this.x, "y:", this.y);
    };
    getDistances = (p: IPoint3) => {
        // return Math.pow(p.x - this.x, 2) + Math.pow(p.y - this.y, 2);
        return 0;
    };
}
const point3 = new Point3(20, 50);

// Access Modifier 访问修饰符

// point3.x = 30;  //
// point3.y = 5;    //直接外部直接访问 class 内部变量,甚至重新赋值是很危险额,为了避免外部直接操作 class 内部属性 这个时候就需要用到 Access Modifier 访问修饰符 对内部变量加以限制
// 有三个   public(常见)、private(常见)、protected
// class 的成员变量,成员方法都是 public
// 所以
// 当 constructor 参数使用的是 public ,当编辑器输入point3. 的时候会出现提示 所有的成员变量和方法
// 当 constructor 参数使用的是 private ,当编辑器输入 point3.x || point3.y 的时候会出现提示 属性“x”为私有属性,只能在类“Point3”中访问。也就是只有在class 内部使用this才能访问的变量
// point3.x
// 例如  drawPoint = () => { console.log("x:", this.x, "y:", this.y); }; 一样使用this 访问成员参数,这样的好处可以防止我们从外部直接访问私有的属性变量,避免肯可能存在的问题
// 但是  当 constructor 参数使用的是 private  class Point3 报错了类“Point3”错误实现接口“IPoint3”。属性“x”在类型“Point3”中是私有属性,但在类型“IPoint3”中不是。
// 也就是说当 constructor 参数使用的是 private 的时候我们的的 类设置了私有属性成员,那么 interface(接口) 都是定义共有属性额方法,最简单的办法是在interface(接口)删除对应的私有属性
// 再来问题是,有时候我们也需要从外部属性访问内部属性 getDistances = (p: IPoint3) => {return Math.pow(p.x - this.x, 2) + Math.pow(p.y - this.y, 2);};
// 这时候 getDistances 里面的p.x p.y 就报错了,无法访问到class 的成员变量的,我又要在外部访问成员属性,又要保持他的私有隔离
// 这时候就需要用到 getter 和 setter 的方法了

// point3.x = 30;
// point3.y = 5;
// point3.x = -30;  //这个时候万万一是负数就很容易出现问题了 可以创建一个setter放了

interface IPoint4 {
    // x: number;
    // y: number;
    drawPoint: () => void;
    getDistances: (p: IPoint4) => number;
    getX: () => number;
    getY: () => number;
    setX: (value) => void;
    setY: (value) => void;
}

class Point4 implements IPoint4 {
    // x: number;
    // y: number;
    // 因为 x,y 都是公有属性 ,所以使用 public,当我们在构造函数中是有访问修饰符,修饰参数的同时,constructor 构造函数也会帮我们自动生成他们所对应的成员变量.
    constructor(private x: number, private y: number) {
        // constructor 构造函数也会帮我们成员变量赋值吗,但是这种时候就不能使用参数缺省的方法了 => x?: number, y?: number
        // this.x = x;
        // this.y = y;
    }
    drawPoint = () => {
        console.log("x:", this.x, "y:", this.y);
    };
    getDistances = (p: IPoint4) => {
        return Math.pow(p.getX() - this.x, 2) + Math.pow(p.getY() - this.y, 2);
        // return Math.pow(p.x - this.x, 2) + Math.pow(p.y - this.y, 2);
        // return 0;
    };
    setX = (value: number) => {
        if (value < 0) {
            throw new Error("value不能小于0");
        }
        this.x = value;
    };
    getX = () => {
        return this.x;
    };
    setY = (value: number) => {
        if (value < 0) {
            throw new Error("value不能小于0");
        }
        this.y = value;
    };
    getY = () => {
        return this.y;
    };
}
const point4 = new Point4(20, 50);
// point4.setX(10);
// point4.setX(-9);编辑器没有报错,但是运行之后就会报错了

// 同时getter 也是一样
// let X = point4.getX(); //获取x的值
// let Y = point4.getY(); //获取y的值

// 同时还有更好的写法

interface IPoint5 {
    // x: number;
    // y: number;
    drawPoint: () => void;
    getDistances: (p: IPoint5) => number;
    // getX: () => number;
    X: number;
    // setX: (value) => void;
    Y: number;
    // getY: () => number;
    // setY: (value) => void;
}

class Point5 implements IPoint5 {
    // x: number;
    // y: number;
    // 因为 x,y 都是公有属性 ,所以使用 public,当我们在构造函数中是有访问修饰符,修饰参数的同时,constructor 构造函数也会帮我们自动生成他们所对应的成员变量.
    constructor(private _x: number, private _y: number) {
        // constructor 构造函数也会帮我们成员变量赋值吗,但是这种时候就不能使用参数缺省的方法了 => x?: number, y?: number
        // this.x = x;
        // this.y = y;
    }
    drawPoint = () => {
        console.log("x:", this._x, "y:", this._y);
    };
    getDistances = (p: IPoint5) => {
        return Math.pow(p.X - this._x, 2) + Math.pow(p.Y - this._y, 2);
        // return Math.pow(p.x - this.x, 2) + Math.pow(p.y - this.y, 2);
        // return 0;
    };
    // 直接使用方法 不适用箭头函数
    set X(value: number) {
        if (value < 0) {
            throw new Error("value不能小于0");
        }
        this._x = value;
    }
    get X() {
        return this._x;
    }
    set Y(value: number) {
        if (value < 0) {
            throw new Error("value不能小于0");
        }
        this._y = value;
    }
    get Y() {
        return this._y;
    }
}
const point5 = new Point5(20, 50);
point5.X = 10;
point5.Y = 1;
let XX = point5.X;
let YY = point5.Y;

//使用 set X()、get X()、set Y()、set Y()、get Y() 这种方法的的时候 需要编译器是 ECMAScript 5 或者更高的时候
// 这时候可以在终端 输入 => tsc -t es5 main.ts  => node main.js

// Module 模块
// 在开发中,我们会有成千上万的代码甚至文件,和class定义,想要吧他们形成完整的程序就需要用到 Module
// 新建一个 point.ts 的文件 ,把我们的 计算两点之间的距离方法放到 point.ts 文件里
// 但是这代码只会在 point.ts 文件使用,这时候其他地方想使用,就在 class 前加上 export 就可以了

import { Point6 } from "./point";
const point6 = new Point6(20, 50);
point6.X = 10;
point6.Y = 1;
let XX6 = point6.X;
let YY6 = point6.Y;
console.log(XX6, YY6);
// 输入 => tsc -t es5 main.ts  => node main.js => log 10 1
```
### import { Point6 } from "./point";

```
interface IPoint6 {
    // x: number;
    // y: number;
    drawPoint: () => void;
    getDistances: (p: IPoint6) => number;
    // getX: () => number;
    X: number;
    // setX: (value) => void;
    Y: number;
    // getY: () => number;
    // setY: (value) => void;
}

export class Point6 implements IPoint6 {
    // x: number;
    // y: number;
    // 因为 x,y 都是公有属性 ,所以使用 public,当我们在构造函数中是有访问修饰符,修饰参数的同时,constructor 构造函数也会帮我们自动生成他们所对应的成员变量.
    constructor(private _x: number, private _y: number) {
        // constructor 构造函数也会帮我们成员变量赋值吗,但是这种时候就不能使用参数缺省的方法了 => x?: number, y?: number
        // this.x = x;
        // this.y = y;
    }
    drawPoint = () => {
        console.log("x:", this._x, "y:", this._y);
    };
    getDistances = (p: IPoint6) => {
        return Math.pow(p.X - this._x, 2) + Math.pow(p.Y - this._y, 2);
        // return Math.pow(p.x - this.x, 2) + Math.pow(p.y - this.y, 2);
        // return 0;
    };
    // 直接使用方法 不适用箭头函数
    set X(value: number) {
        if (value < 0) {
            throw new Error("value不能小于0");
        }
        this._x = value;
    }
    get X() {
        return this._x;
    }
    set Y(value: number) {
        if (value < 0) {
            throw new Error("value不能小于0");
        }
        this._y = value;
    }
    get Y() {
        return this._y;
    }
}
// const Point6 = new IPoint6(20, 50);
```



### Generics 泛型 

在之前的数组我们有接触过

```
/ 如 :
// 定义一个数组
let lst: number[] = [1, 2, 3, 4];
let lst2: Array<number> = [1, 2, 3, 4]; //这种泛型
```

在 TypeScript 中 类型加上 箭头括号的 表达方式就是泛型 ,泛型就相当于一个数据的模版,往模版里面输入什么类型, 他就会根据你的输入生成相对应的、具体的、确定的、数据类型

#### 泛型的具体用法
```
let lastInArray = (arr: Array<number>) => {
    return arr[arr.length - 1];
};
const l1 = lastInArray([1, 2, 3, 4]);
// 当这时候我们想要 lastInArray 不仅能输入数字,也能是字符串的类型
// 很多人直接觉得 Array<any> 就解决了,

let lastInArray1 = (arr: Array<any>) => {
    return arr[arr.length - 1];
};
const l2 = lastInArray1(["a", "b", "c", "d"]);
// 但是如果是强类型定义,这是不足够的,这时候就是 泛型 大显身手的时候了
// 这里 <T> 表示 动态类型泛型 ,T可以自己定义名称,但是T是泛型 约定熟成的写法
let lastInArray2 = <T>(arr: Array<T>) => {
    return arr[arr.length - 1];
};
const l3 = lastInArray([1, 2, 3, 4]);
const l4 = lastInArray2(["a", "b", "c", "d"]);

// 也可简写完成
let lastInArray3 = <T>(arr: T[]) => {
    return arr[arr.length - 1];
};
const l5 = lastInArray([1, 2, 3, 4]);
const l6 = lastInArray3(["a", "b", "c", "d"]);
```

1. 这时候鼠标指向 l5 就会看到 l5 是 number 类型.鼠标放在 l6 的时候是 string 类型
2. 所以通过使用 泛型 我们就可以完美的保持代码中的类型一致性了,通过这种一致性可以成倍的提高我们的代码效率
3. 当我们使用泛型的时候,也可以明确的指明泛型的类型 如下

```
const l7 = lastInArray3<string>(["a", "b", "c", "d"]);
const l8 = lastInArray3<string | number>(["a", "b", "c", "d"]);
// 在我们l8参数中只输入了都是string的类型的时候,仅仅使用ts的自动识别类型,是不足够的,因为自动匹配类型我们只能得到string ,指明类型以后得到的是 string | number 的类型

let MakeTuple = (x: number, y: string) => [x, y];
// 那么我们在调用这个函数的时候,我想给x,y,都安排不同的 泛型 类型 如下:
let MakeTuple1 = <T, Y>(x: T, y: Y) => [x, y];
const v1 = MakeTuple1(1, "one");
const v2 = MakeTuple1(true, 1);
const v3 = MakeTuple1<boolean, number>(true, 1); //指定类型 箭头括号类型逗号隔开就行了

// 泛型 没有给指定的类型,他会制动给他识别对应的类型,我们也可也设置默认的类型
let MakeTuple2 = <T, Y = number>(x: T, y: Y) => [x, y];
const v4 = MakeTuple2<boolean>(true, 1); //这个时候就不需要定义 泛型 的第二个类型了
```


这些是我在学习中的总结出来的,希望能有帮助。。。























最后修改:2022 年 03 月 11 日
如果觉得我的文章对你有用,请随意赞赏