① ts基础

前言

  • npm i -g typescript
  • $tsc -v
tsc test.ts // 编译
node test.js // 执行

const hello: string = 'Hello World!'
console.log(hello)

1 对象+基本类型

1.1 ts是一种面向对象的编程语言

  • 面向对象有两个概念: 对象 + 类

    • 类:一个模板,描述一类对象的行为和状态

    • 对象: 类的一个实例

class Site {
    name(): void {
        console.log('yaoyao')
    }
}
var obj = new Site();
obj.name();

1.2 ts的基础类型

ts的基础类型 描述
any 任意类型
number 数字
string 字符
boolean 布尔
enum 枚举
void 用于标识方法返回值的类型,表示该方法没有返回值
null 表示对象值缺失
undefined 用于初始化变量为一个未定义的值
never 其他类型的子类型,代表从不会出现的值
1.2.1 any
  • any是ts针对编程时类型不明确的变量使用的一种数据类型

  • 变量的值会动态改变时

  • 任意值类型可以让这些变量跳过编译阶段的类型检查

let x: any = 1; // 数字类型
x = 'i am who i am'; // 字符串类型
x = false; // 布尔类型

let x: any = 4;
x.ifExists(); // 正确,这里不会检查ifExists方法是否存在
x.toFixed(); // 正确

let arrayList: any[] = [1, false, 'fine'];
1.2.2 空校验(strictNullChecks)特性
  • 在ts中启用严格的空校验(strictNullChecks)特性,可以使得null undefined只能被赋值给void或本身对应的类型
let x: number;
x = 1; // 运行正确
x = undefined; // 运行错误
x = null; // 运行错误

let x: number | null | undefined;
x = 1; // 运行正确
x = undefined; // 运行正确
x = null; // 运行正确
1.2.3 never
  • never 是其他类型的子类型,代表不会出现的值

  • 声明为never类型的变量只能被never类型所赋值

  • 在函数中通常表现为抛出异常或无法执行到终止点(无限循环)

let x: never;
let y: number;

x = 123; // 运行错误
x = (() => { throw new Error('exception') })(); // 运行正确
// 运行正确 never类型可以赋值给数字类型
y = (() => { throw new Error('exception') })(); 

function error(message: string): never {
    throw new Error(message);
}

function loop(): never {
    while(true) {}
}

2 ts函数

/**
 * 函数定义 函数返回值
 * 1 return_type是返回值的类型
 * 2 返回值的类型需要与函数定义的返回类型(return_type)一致
 * */ 
// function function_name() [:return_type] {
//     // 执行代码
//     [return value;]
// }
function greet(): string {
    return 'hello world';
}
function caller() {
    var msg = greet();
    console.log(msg)
}
caller()

2.1 带参函数

/**
 * 带参数函数
 * param1、param2 为参数名
 * datatype为参数类型
*/
// function func_name(param1[: datatype], param2[: datatype]) {
// }
function add(x: number, y: number): number {
    return x + y;
}
console.log(add(1, 2));

2.2 可选参数

  • 在ts函数里,如果我们定义了参数,则必须传入这些参数

  • 除非将这些参数设置为可选,可选参数使用问号标识?

  • 可选参数必须跟在必需参数后面

function buildName(firstName: string, lastName: string) {
    return firstName + ' ' + lastName;
}
let result1 = buildName('Bob'); // 错误
let result2 = buildName('Bob', 'Adams', 'sr.'); // 错误
let result3 = buildName('Bob', 'Adams'); // 正确

function buildName2(firstName: string, lastName?: string) {
    return firstName + ' ' + lastName;
}
let result1 = buildName('Bob'); // 正确
let result2 = buildName('Bob', 'Adams', 'sr.'); // 错误
let result3 = buildName('Bob', 'Adams'); // 正确

2.3 默认参数

  • 参数不能同时设置为可选和默认
// function function_name(param1[: type], param2[: type] = default_value) {
// }

function calculate_discount(price: number, rate: number = 0.50) {
    var discount = price * rate;
    console.log('计算结果:', discount);
}
calculate_discount(1000);
calculate_discount(1000, 0.30);

2.4 剩余参数

  • 允许我们将一个不确定数量的参数作为一个数组传入

  • 函数的最后一个命名参数restOfName以...为前缀,它将成为一个由剩余参数组成的数组,索引值从0到resOfName.length

function buildName(firstName: string, ...resOfName: string[]) {
    return firstName + ' ' + resOfName.join('');
}
let employeeName = buildName('Joseph', 'Same', 'Lucas', 'Andy');

function addNumbers(...nums: number[]) {
    var i;
    var sum: number = 0;
    for(i = 0; i < addNumbers.length; i++) {
        sum = sum + nums[i];
    }
    console.log('和为:', sum)
}
addNumbers(1, 2, 3)
addNumbers(10, 10, 11, 111, 1111)

2.5 匿名函数

  • 在程序运行时动态声明,除了没有函数名外,其他的与标准函数一样
// var res = function([arguments]) { ... }

var msg = function() {
    return 'hello world';
}
console.log(msg())

var res = function(a: number, b: number) {
    return a * b;
};
console.log(res(12, 2))

##### 匿名函数自调用

```ts
(function() {
    var x = 'hello';
    console.log(x)
})()

2.6 构造函数

/**
 * 构造函数
 * arg1,arg2,...argN: 参数列表
 * functionBody: 一个含有包括函数定义的js语句的字符串
*/
// var res = new Function([arg1[, arg2[, ...argN]],] functionBody)

var myFunction = new Function('a', 'b', 'return a * b');
var x = myFunction(4, 3);
console.log(x);

2.7 递归函数

  • 在函数内调用函数本身
function factorial(number) {
    if(number <= 0) {
        return 1;
    } else {
        return (number * factorial(number - 1))
    }
}
console.log(factorial(6));

2.8 Lambda函数,即箭头函数

// ([param1, param2, ...param n]) => statement;
// ([param1, param2, ...param n]) => { // 代码块 }
var foo = (x: number) => 10 + x;
console.log(foo(100));

var foo2 = (x: number) => {
    x = 10 + x;
    console.log(x);
}
foo2(100);

var func = x => {
    if(typeof x == 'number') {
        console.log(x + '是一个数字');
    } else if(typeof x == 'string') {
        console.log(x + '是一个字符串');
    }
}
func(12);
func('Tom');

var disp = () => {
    console.log('Function invoked');
}
disp();

2.9 函数重载

  • 重载是方法名相同,而参数不同,返回类型可以相同也可以不同

  • 每个重载的方法都必须有一个独一无二的参数类型列表

  • 参数类型不同,则参数类型应设置为any

  • 参数数量不同,可以将不同的参数设置为可选

2.9.1 参数类型不同
  • function disp(string): void;
  • function disp(number): void;
2.9.2 参数数量不同
  • function disp(n1: number): void;
  • function disp(x: number, y: number): void;
2.9.3 参数类型顺序不同
  • function disp(n1: number, s1: string): void;
  • function disp(s: string, n: number): void;
function disp2(s1: string): void;
function disp2(n1: number,s1: string): void;
function disp2(x: any, y?: any): void {
    console.log(x);
    console.log(y);
}
disp2('abc');
disp2(1, 'xyz');

3 元组

  • 数组中元素类型一般是相同的,如果存储的元素类型不同,则需要使用元组

  • 元组中允许存储不同类型的元素,元组可以作为参数传递给函数

  • 语法:var tuple_name = [value1, value2, value3, ...value n]

var mytuple = [10, 'yaoyao'];

var mytuple1 = [];
mytuple1[0] = 120;
mytuple1[1] = 220;

3.1 访问元组

var mytuple2 = [10, 'yaoyao'];
console.log(mytuple2[0]);

3.2 解构元组

var a = [10, 'yaoyao'];
var [b, c] = a;
console.log(b);
console.log(c);

4 联合类型

  • 联合类型可通过管道(|)将变量设置多种类型,赋值时可以根据设置的类型来赋值

注意:只能赋值指定类型,否则报错

  • 语法: Type1|Type2|Type3
var val: string | number;
val = 12;
console.log('数字为:' + val);
val = 'yaoyao';
console.log('字符串为:' + val);
// var val:string|number;
// val = true; // 报错

4.1 将联合类型作为函数参数使用

function disp(name: string | string[]) {
    if(typeof name == 'string') {
        console.log(name);
    } else {
        var i;
        for(i = 0; i < name.length; i++) {
            console.log(name[i]);
        }
    }
}
disp('yaoyao');
console.log('输出数组...');
disp(['hello', 'yaoyao', 'are', 'you', 'ok'])

4.2 联合类型数组

var arr: number[] | string[];
var i: number;
arr = [1, 2, 4];
console.log('**数字数组**');
for(i = 0; i<arr.length; i++) {
    console.log(arr[i]);
}
arr = ['yaoyao', 'hello', 'world'];
console.log('**字符串数组**');
for(i = 0; i<arr.length; i++) {
    console.log(arr[i]);
}

5 接口interface

  • 接口是一系列抽象方法的声明,是一些方法特征的集合,这些方法都应该是抽象的,需要具体类去实现,三方可通过这组抽象方法调用,让具体的类执行具体的方法

接口不能转换为js,它只是ts的一部分

  • 定义:interface interface_name {}
interface IPerson {
    firstName: string,
    lastName: string,
    sayHi: ()=> string
}
var customer: IPerson = {
    firstName: 'Tom',
    lastName: 'Hanks',
    sayHi: (): string => 'Hi there'
}

console.log('Customer 对象 ');
console.log(customer.firstName);
console.log(customer.lastName);
console.log(customer.sayHi());

var employee: IPerson = {
    firstName: 'Zhou',
    lastName: 'yaoyao',
    sayHi: (): string => 'Hello!!'
}

console.log('Employee 对象 ');
console.log(employee.firstName);
console.log(employee.lastName);
console.log(employee.sayHi());

6 对象

  • 对象是包含一组键值对的实例。值可以是标量、函数、数组、对象等
var obj_name = {
    key1: 'value1', // 标量
    key2: 'value',
    key3: function() {},
    key4: ['content1','content2']
}

var sites1 = {
    site1: 'zhou',
    site2: 'yaoyao'
}
console.log(sites1.site1);
console.log(sites1.site2);

6.1 类型模板

var sites2 = {
    site1: 'zhou',
    site2: 'yaoyao',
    sayHello: function() {} // 类型模板
}
sites2.sayHello = function() {
    console.log('hello' + sites2.site2);
}
sites2.sayHello()

6.2 对象也可以作为一个参数传递给函数

var sites3 = {
    site1: 'zhou',
    site2: 'yaoyao'
}
var invokesites = function(obj: {site1: string, site2: string}) {
    console.log('site1:'+obj.site1);
    console.log('site2:'+obj.site2);
}
invokesites(sites3)

6.3 鸭子类型

  • 关注点在于对象的行为,能做什么;而不是关注对象所属的类型

  • 鸭子类型是动态类型的一种风格,是多态的一种形式

  • 在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由“当前方法和属性的集合”决定

  • 多态就是允许方法重名 参数或返回值可以是父类型传入或返回。

interface IPoint {
    x: number
    y: number
}
function addPoints(p1: IPoint, p2: IPoint): IPoint {
    var x = p1.x + p2.x
    var y = p1.y + p2.y
    return {x, y}
}
var newPoint = addPoints({x:3, y:4},{x:5, y:1})
console.log('newPoint', newPoint);

// var newPoint2 = addPoints({x:3},{x:5, y:1}) // 错误

7 类

  • ts是面向对象的js

  • 类描述了所创建的对象共同的属性和方法

ts支持面向对象的所有特性

  • 定义: calss class_name { // 类作用域 }

  • 类可以包含以下几个模块(类的数据成员):

    1. 字段--字段是类里面声明的变量。字段表示对象的有关数据

    2. 构造函数--类实例化时调用,可以为类的对象分配内存

    3. 方法--方法为对象要执行的操作

7.1 创建类的数据

  • this关键字表示当前类实例化的对象

  • 构造函数的参数名与字段名相同

class Car {
    // 字段
    engine: string;
    // 构造函数
    constructor(engine: string) {
        this.engine = engine
    }
    // 方法
    disp(): void {
        console.log('发动机为:' + this.engine);
    }
}

7.2 创建实例化对象

  • var object_name = new class_name([arguments])

类中的字段属性和方法可以使用.来访问

  • obj.field_name

  • obj.function_name()

var obj = new Car('Engine 1')

class Car2 {
    engine: string;
    constructor(engine: string) {
        this.engine = engine
    }
    disp(): void {
        console.log('函数中显示发动机型号:' + this.engine);
    }
}

var obj = new Car('xxxsy1');
console.log('读取发动机型号:' + obj.engine);

obj.disp()

7.3 类的继承extends

  • 子类除了不能继承父类的私有成员(方法和属性)和构造函数,其他都可以继承

  • ts一次只能继承一个类,不支持继承多个类;但ts支持多重继承(A继承B,B继承C)

  • 语法:class child_class_name extends parent_class_name

class Shape {
    Area: number 
    constructor(a: number) {
        this.Area = a
    }
}
class Circle extends Shape {
    disp(): void {
        console.log('圆的面积:' + this.Area);
        
    }
}
var obj11 = new Circle(233);
obj11.disp();

class Root {
    str: string;
}
class Child extends Root {}
class Leaf extends Child {}

var obj22 = new Leaf()
obj22.str = 'Hello'
console.log(obj22.str);

7.4 继承类的方法重写

  • supper关键字是对父类的直接引用,该关键字可以引用父类的属性和方法
class PrinterClass {
    doPrint(): void {
        console.log('父类的doPrint()方法');
    }
}
class StringPrinter extends PrinterClass {
    doPrint(): void {
        super.doPrint()
        console.log('子类的dePrint()方法');
    }
}

7.5 static 关键字

  • 用于定义类的数据成员(属性和方法)为静态,静态成员可以直接通过类名调用
class StaticMen {
    static num: number;
    static disp(): void {
        console.log('num值为:' + StaticMen.num);
    }
}
StaticMen.num = 12
StaticMen.disp() // num值为12

7.6 instanceof 运算符

  • 用于判断对象是否是指定的类型,如果是返回true,否则返回false
class PersonI {}
var objI = new PersonI()
var isPersonI = objI instanceof PersonI;
console.log('objI对象是PersonI类实例化来的吗?' + isPersonI);

7.7 访问控制修饰符

  • ts中可使用访问控制符来保护类、对象、方法和构造方法的访问

  • ts支持3种不同的访问权限:

    1. public(默认) 公有,可以在任何地方被访问

    2. protected 受保护,可以被其自身以及其子类和父类访问

    3. private 私有,只能被其定义所在的类访问

class Encapsulate {
    str1: string = 'Hello';
    private str2: string = 'world';
}
var objII = new Encapsulate()
console.log(objII.str1);
// console.log(objII.str2);

7.8 类和接口

  • 类可以实现接口,使用关键字 implements
interface ILoan {
    interest: number
}
class AgriLoan implements ILoan {
    interest: number
    rebate: number
    constructor(interest: number, rebate: number) {
        this.interest = interest
        this.rebate = rebate
    }
}
var objIl = new AgriLoan(10, 1)
console.log('利润为:' + objIl.interest + ',抽成为:' + objIl.rebate);

8 命名空间

  • 关键字 namespace

  • 目的:解决重名问题

  • 命名空间定义了标识符的可见范围,一个标识符可在多个名字空间中定义,且在不同名字空间中的含义是互不相干的

  • 在外部调用命名空间中的类和接口,需要在类和接口添加extends关键字

  • 如果一个命名空间在一个单独的ts文件中,则应使用三斜杠///应用它

    • /// <reference path = 'SomeFileName.ts' />
// IShape.ts文件
namespace Drawing {
    export interface IShape {
        draw()
    }
}
// Circle.ts文件
/// <reference path = 'IShape.ts'/>
namespace Drawing {
    export class Circle implements IShape {
        public draw() {
            console.log('Circle is drawn');
        }
    }
}
// Triangle.ts文件
/// <reference path = 'IShape.ts'/>
namespace Drawing {
    export class Triangle implements IShape {
        public draw() {
            console.log('Triangle is drawn'); 
        }
    }
}
// TestShape.ts文件
/// <reference path = 'IShape.ts'/>
/// <reference path = 'Circle.ts'/>
/// <reference path = 'Triangle.ts'/>
function drawAllShapes(shape: Drawing.IShape) {
    shape.draw()
}
drawAllShapes(new Drawing.Circle())
drawAllShapes(new Drawing.Triangle())

// tsc --out nameSpaceTest.js TestShape.ts
// node nameSpaceTest.js

8.1 嵌套命名空间

namespace namespace_name1 {
   export namespace namespace_name2 {
     export class class_name {}
   }
}
  • 成员的访问使用点好.来实现
// Invoice.ts文件
namespace Runoob {
    export namespace invoiceApp {
        export class Invoice {
            public calculateDiscount(price: number) {
                return price * 4
            }
        }
    }
}
// IvoiceTest.ts文件
/// <reference path = 'Invoice.ts'/>
var invoice = new Runoob.invoiceApp.Invoice()
console.log(invoice.calculateDiscount(500));

9 声明文件

9.1 栗子

  • js中 使用jQuery
    • $('#foo);
    • jQuery('#foo')
  • ts中
    • jQuery('#foo') // index.ts(1,1): err: Cannot find name 'jQuery'
    • 使用declare关键字来定义它的类型,帮助ts判断我们传入的参数类型
    • declare var jQuery: (selector: string) => any;
    • jQuery('#foo')

declare定义的类型只会用于编译时的检查,编译结果会被删除

9.2 声明文件

  • 声明文件以.d.ts为后缀

  • 声明文件不包含实现,只是类型声明

  • 声明文件或模板的语法格式: declare module Module_Name {}

  • ts引入声明文件语法格式: ///

// CalcThirdPartyJsLib.js文件
var Yaoyao;
(function(Yaoyao) {
    var Calc = (function() {
        function Calc() {}
    })
    Calc.prototype.doSum = function(limit) {
        var sum = 0;
        for(var i=0; i<=limit; i++) {
            sum = sum + i
        }
        return sum;
    }
    Yaoyao.Calc = Calc;
    return Calc;
})(Yaoyao || (Yaoyao = {}))
var test = new Yaoyao.Calc()

//Calc.d.ts文件
declare module Yaoyao {
    export class Calc {
        doSum(limit: number): number
    }
}
// 把声明文件加入到ts中
// CalcTest.ts文件
/// <reference path = 'Calc.d.ts'/>
var obj = new Yaoyao.Calc()
console.log(obj.doSum(10));
posted on 2021-07-07 00:07  pleaseAnswer  阅读(390)  评论(0编辑  收藏  举报