day134:2RenMJ:TypeScript的抽象类&装饰器&命名空间&模块&编译配置文件&python中的类型注解

目录

1.抽象类

2.装饰器

3.命名空间

4.模块

5.编译配置文件

6.python的类型注解

1.抽象类

抽象类(abstract class)做为其它派生类的基类使用。 它们一般不会直接被实例化。 不同于接口,抽象类可以包含成员的实现细节。 abstract关键字是用于定义抽象类和在抽象类内部定义抽象方法。

代码如下所示:

// 抽象父类
abstract class Animal{
    abstract makeSound(): void;        // 抽象方法,没有函数体
    constructor(public name: string){  // 抽象类的构造方法

    }
    desc(): void {   // 抽象类中也可以定义子类的公共方法或公共属性
        console.log('roaming the earch...');
    }
}

// 抽象子类
abstract class Dog extends Animal{
    abstract move(): string;
}

// 具象类/具体类
class ChineseGardenDog extends Dog{
    public name:string;
    makeSound(){
        return "汪汪汪~"
    }
    move(): string {
        return "奔跑中...."
    }
    constructor(name: string){
        super(name); // 继承了抽象类的子类,必须对父类进行初始化
    }
}

var dog = new ChineseGardenDog("来福");
console.log(dog.name);
console.log(dog.makeSound());

2.装饰器

随着TypeScript和ES6里引入了类,在一些场景下我们需要额外的特性来支持标注或修改类及其成员。 装饰器(Decorators)为我们在类的声明及成员上通过元编程语法添加标注提供了一种方式。装饰器是一种特殊类型的声明,它能够被附加到类声明方法访问符属性参数

代码如下所示:

function derator1() {
    console.log("derator1(): evaluated");
    return function (target, propertyKey: string, descriptor: PropertyDescriptor) {
        console.log("derator1(): called");
    }
}

function derator2(name:string) {
    console.log(`derator2(): evaluated,name=${name}`);
    return function (target, propertyKey: string, descriptor: PropertyDescriptor) {
        console.log(`derator2(): called,name=${name}`);
    }
}

class Demo {
    @derator1()
    @derator2("xiaoming")
    show() {
        console.log("show()执行了")
    }
}

var d = new Demo();
d.show();

/*
 * 输出结果顺序:
 * derator1(): evaluated
 * derator2(): evaluated,name=xiaoming
 * derator2(): called,name=xiaoming
 * derator1(): called
 * show()执行了
 *
 */

3.命名空间

当项目大了以后,需要创建和声明的函数,类就多了,自然人也就多了,人多就坏事。想想全国有几个张三?

命名空间(namespace)一个最明确的目的就是解决重名问题

命名空间定义了标识符的可见范围,一个标识符可在多个名字空间中定义,它在不同名字空间中的含义是互不相干的。这样,在一个新的名字空间中可定义任何标识符,它们不会与任何已有的标识符发生冲突,因为已有的定义都处于其他名字空间中。

1.定义一个命名空间

// 命名空间的名称采用驼峰式写法
namespace App{ 
    // 需要在命名空间外部可以调用 当前命名空间的类,函数和接口等,则需要在左边添加 export 关键字。
    // 变量
    export var username:string="App空间的变量";
    // 常量,一旦定义以后,不能修改值
    export const NAME = "App命名空间的常量";
    // 函数
    export function func(){
        return "App命名空间里面的func"
    }
    //
    export class Humen{

    }
    // 当然,在当前命名空间下也是可以执行代码的    
}

2.导入命名空间

main.ts,代码:

导入其他命名空间的格式:/// <reference path = "文件名" />,可以导入多个命名空间,一行一个。

/// <reference path="app.ts" />
console.log(App.func());  // 调用其他命名空间的内容,必须以"命名空间的名称.xxxx"格式进行调用
console.log(App.NAME);
console.log(App.username);
console.log(new App.Humen());

使用了命名空间以后,编译命令需要稍微调整如下:

tsc --out main.js main.ts  # 必须指定--out参数才能正常编译

4.模块

TypeScript 模块的设计理念是可以更换的组织代码。模块是在其自身的作用域里执行,并不是在全局作用域,这意味着定义在模块里面的变量、函数和类等在模块外部是不可见的

typescript提供了两种模块:内部模块外部模块,因为外部模块需要依赖第三方框架才可以使用,例如:commonjs,requirejs等。所以在此,我们只简单介绍内部模块的声明和使用。

1.内部模块

1.声明模块

app.ts,代码

module App{
    export class Humen {
        desc(){
            console.log("hello");
        }
    }

    export function func(){
        console.log("hello, func");
    }

}

2.调用模块

main.ts,代码

/// <reference path="./app.ts">
var people = new App.Humen();
people.desc();
App.func();

使用了内部模块以后,编译命令和命名空间一样:

tsc --out main.js main.ts

2.外部模块

out.js,代码

class Humen{
    uname:string;
    constructor(uname){
        this.uname = uname;
    }
    desc() {
        return `您好,我叫 ${this.uname}`;
    }
}
export { Humen };
export { Humen as People };

导入模块,main.ts,代码:

import { Humen, People } from "./Out";
let obj1 = new Humen("小白");
let obj2 = new People("小黑");
obj1.desc();
obj2.desc();

编译命令:

tsc --module es6 main.ts   # --module 表示代码中编写模块的规范和标准

5.编译配置文件

基于typescript开发的项目根目录,一般都会存在一个文件,叫tsconfig。这是typescript的编译配置文件。

配置选项:https://www.tslang.cn/docs/handbook/compiler-options.html

tsconfig.json,常用配置项说明,代码:

// 当前配置文件名必须固定是: tsconfig.json
// 同时,json文件中不能出现注释的,所以此处的注释仅仅是为了学习,开发中决不能有
{
    "compilerOptions": {
        "module": "system",     // 项目中编写模块的规范标准
        "noImplicitAny": true,  // 表达式或声明上有隐含的 any类型时报错
        "removeComments": true, // 删除所有注释,除了以 /!*开头的版权信息。
        "preserveConstEnums": true,  // 保留const和Enums声明
        "outDir": "script",  // 编译结果保存目录
        // "outFile": "../../built/local/tsc.js",  // 编译以后输出的文件,一般用不上
        "sourceMap": true,  // 生成相应的 .map文件
        "experimentalDecorators": true,  // 启用实验性的ES装饰器
        "lib": [ // 编译过程中需要引入的库文件的列表
            "es5",
            "dom",
            "es2015.promise"
        ]
    },
    "files": [   // 指定要编译的文件列表, 与include和exclude冲突,开发中,一般使用exclude
      "main.ts"
    ]
  //    "include": [ // 指定要编译的文件所在目录
////        "src/**/*",
//        "./"
//    ],
//    "exclude": [ // 指定在编译时排除的文件目录
//        "node_modules",
//        "**/*.spec.ts"
//    ]
}

6.python的类型注解

1.typing模块

自python3.5开始,PEP484为python引入了类型注解(type hints)

  • 类型检查,防止运行时出现参数和返回值类型、变量类型不符合。

  • 作为开发文档附加说明,方便使用者调用时传入和返回参数类型。

  • 该模块加入后并不会影响程序的运行,不会报正式的错误,只有提醒。

    pycharm目前支持typing检查,参数类型错误会黄色提示。

2.常用类型

  • int,long,float: 整型,长整形,浮点型

  • bool,str: 布尔型,字符串类型

  • List, Tuple, Dict, Set:列表,元组,字典, 集合

  • Iterable,Iterator:可迭代类型,迭代器类型

  • Generator:生成器类型

3.基本类型指定

from typing import List,Dict,Tuple,Union
# 整型
num:int = 100
# 字符串
data:str = "200"
# 布尔值
bo:bool = True
# 列表
data_list:List[str] = ["1","2","3"]
# 字典
data_dict:Dict[str, str] = {"name":"xiaoming",}
# 元组[限制数据和类型]
data_tuple:Tuple[int,int,bool] = (1,2,False)

# 联合类型[泛型]
U1 = Union[str,int] # 只能是字符串或者整形
data_union1:U1 = "20"
data_union2:U1 = 200
data_union3:U1 = [1,2,3]  # 此处不符合要求,出现黄色提示

def test(a:int, b:str) -> str:
    print(a, b)
    return 1000

if __name__ == '__main__':
    test('test', 'abc')

"""
函数test,
a:int  指定了输入参数a为int类型,
b:str  b为str类型,
-> str  返回值为str类型。

可以看到,
在方法中,我们最终返回了一个int,此时pycharm就会有警告;
当我们在调用这个方法时,参数a我们输入的是字符串,此时也会有警告;
但非常重要的一点是,pycharm只是提出了警告,但实际上运行是不会报错,毕竟python的本质还是动态语言
"""

4.复杂的类型标注

from typing import List
Vector = List[float]

def scale(scalar: float, vector: Vector) -> Vector:
    return [scalar * num for num in vector]

# 类型判定: 传递进行的数据只要类似Vector格式即可,这里也是鸭子类型所导致的.
new_vector = scale(2.0, [1.0, -4.2, 5.4])
from typing import Dict, Tuple, Sequence

ConnectionOptions = Dict[str, str]
Address = Tuple[str, int]
Server = Tuple[Address, ConnectionOptions]

def broadcast_message(message: str, servers: Sequence[Server]) -> None:
    ...

# The static type checker will treat the previous type signature as
# being exactly equivalent to this one.
def broadcast_message(
    message: str,
    servers: Sequence[Tuple[Tuple[str, int], Dict[str, str]]]) -> None:
    ...
):
    ...
"""
这里需要注意,元组这个类型是比较特殊的,因为它是不可变的。
所以,当我们指定Tuple[str, str]时,就只能传入长度为2,
并且元组中的所有元素都是str类型
"""

5.泛型指定

from typing import Sequence, TypeVar

T = TypeVar('T')      # 定义泛型变量,可以是任意类型的数据
b2:T = True # b2的值可以是任意类型数据

def first(l: Sequence[T]) -> T:   # Generic function
    return l[0]

A = TypeVar('A', bool, str, bytes)  # 定义当前泛型的范围只能是字符串或者bytes类型
b3:A = "hello"
b4:A = "hello".encode()
b5:A = True

6.创建变量时的类型指定

from typing import NamedTuple

class Employee(NamedTuple):
    name: str
    id: int = 3

employee = Employee('Guido')
assert employee.id == 3

7.不足之处:提示错误而不影响执行

from typing import List

def test(b: List[int]) -> str:
    print(b)
    return 'test'


if __name__ == '__main__':
    test([1, 'a'])

"""
从这个例子可以看出来,虽然我们指定了List[int]即由int组成的列表,
但是,实际中,只要这个列表中存在int(其他的可以为任何类型),就不会出现警告
"""

 

posted @ 2021-02-01 17:08  iR-Poke  阅读(147)  评论(0编辑  收藏  举报