TSTypeScript语法学习

Posted woodwhale

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了TSTypeScript语法学习相关的知识,希望对你有一定的参考价值。

1、安装typescript

这一篇幅仅仅涉及到ts的基本语法,没有涉及到ts项目以及配置

typescript全局安装,我这里使用的是cnpm,npm或者yarn同理

cnpm install -g typescript
cnpm install -g ts-node
cnpm install -g tslib @types/node	# 包含console等辅助函数的ts库

安装完毕之后输入tsc -v查看版本,同时检查是否安装成功

2、变量

和kotlin一样,使用: (type)来声明变量的类型

2.1 ES6原生类型

如下是ES6中的原生类型

let num: number = 0xdeadbeef
let bol: boolean = true
let str: string = "woodwhale"
let nul: null = null
let und: undefined = undefined
let obj: object = new String("sheepbotany")

2.2 任意类型

任意类型包括两种,一种是any,一种是unknow

其中,any类型可以调用属性和方法,unknow无法调用

let anys: any = "我是任意的,随便赋值"
anys = 114514   // number
anys = false // 布尔
anys = null
anys = undefined
anys = []   // 数组
anys = a: 123, b: ():number => 114514   // 字典
anys.a  // 可以调用属性和方法
anys.b()

let unk: unknown = "未知类型,比any安全"
unk = a: 123
// unk.a  无法调用

再来看第二个区别,any可以当作父类也可以当作子类,unkown只能当作父类、不能当作子类。

如果非要给unknow赋值,可以给其赋值any类型的变量或者unknow类型的变量

let test: any = "test"
let test2: string = "test2"
test2 = test    // 可以
console.log(test2)

let test3: unknown = "test3"
// test2 = test3 不可以
test3 = test    // 可以

2.3 数组类型

两种形式,面向结构和面向对象

let str_arr: string[] = ["1","1","4","5","1","4"]
let num_arr: number[] = [1,1,4,5,1,4]
let any_arr: any[] = [1,"1","4",[5],"1",4]
let bol_arr: boolean[] = [true,false]

let obj_arr: Array<string> = str_arr	// Array接口
console.log(obj_arr)

如果是多维数组,使用下面的形式

let str2_arr: string[][] = [["1"],["2"]]
let obj2_arr: Array<Array<string>> = str2_arr
console.log(obj2_arr)

在ES6中多了一个...的扩展运算符,用于取出参数对象中的所有可便利的属性,可以配合数组使用,这里拿函数举一个例子

function fun_arr(...args: any): void 
    console.log(args)   // 传入参数的数组
    console.log(arguments)  // 内置的arguments对象


fun_arr(1,1,4,5,1,4)
/*
    [ 1, 1, 4, 5, 1, 4 ]
    [Arguments]  '0': 1, '1': 1, '2': 4, '3': 5, '4': 1, '5': 4 
*/

除此之外,我们可以使用接口来定义自己的数组

interface myNumberArr 
    [arr_index: number]: number


let my_arr: myNumberArr = [1,2,3,4]
console.log(my_arr)

/*
	[1,2,3,4]
*/

2.4 联合类型

在ts中,声明变量的类型可以使用标识符 | 来进行类型的联合,例如我需要一个变量可以置为字符串,同时也可以置为数字,那么可以这样使用

// 联合类型,可以是字符串,也可以是数字
let phone_number: number | string = "1919810"
phone_number = 114514

// 联合类型使用的小例子
let fn = (flag: number | boolean): boolean => 
    return !!flag

console.log(fn(1) === fn(true))

2.5 交叉类型

和联合类型相反,交叉类型必须满足多种类型的需求,必须同时满足

interface People 	// 人 的接口
    name: string,
    age: number

interface Man 		// 男人 的接口
    sex: string

const woodwhale = (man: People & Man): void => 
    console.log(man)

woodwhale(
    name: "woodwhale",
    age: 19,
    sex: "我太男了"
)
/*
     name: 'woodwhale', age: 19, sex: '我太男了' 
*/

2.6 类型断言

这里所谓的断言可以理解为强制转换,使用关键字as来进行强转,或者使用<type>来进行强转

let assertion_fun = (num: number | string):void => 
    console.log((num as string).length) // 将num强转为string类型
    console.log(<number>num)    // num强转为number类型

assertion_fun("114514")
/*
    6
    114514
*/

2.7 自动推断

如果我们在ts中没有声明变量的类型,ts会进行自动推断

let _str = "woodwhale"   // 自动推断为string类型
let _num = 114514   // 自动推断为数字类型
let _any    // 自动推断为any类型

2.8 类型别名

在ts中使用type关键字可以进行类型起别名的操作,这里演示联合类型与类型别名的使用

type sn = string | number

let str_or_num: sn = "114514"
str_or_num = 1919810

type还可以定义函数类型

type fun = () => string // 一个 无参数的 返回string类型的 方法

let my_fun: fun = function f() 
    return "114514"

// 或者匿名函数
let my_fun_without_name: fun = () => "1919810"

console.log(my_fun())
/*
	114514
*/

type还可以指定变量的种类,起到限定的作用

type flag = true | false | "true" | "false" | 1 | 0

let flag:flag = true

2.9 元组类型

typescript中有一种类型叫做元组类型,是数组的变种,可以存放不同数据类型的数据

let arr:[string,number] = ["woodwhale",19]

console.log(arr)
/*
	[ 'woodwhale', 19 ]
*/

arr.push("sheepbotany",21)

console.log(arr)
/*
	[ 'woodwhale', 19, 'sheepbotany', 21 ]
*/

如果需要添加数据到元组中,使用push方法就可以,但是必须是申明过的数据类型之一(这里必须是string或number)

2.10 never类型

在ts中,never类型表示不可能达到的一种类型,例如既要满足是string,又要满足是number的交叉类型

type flag = true | false | "true" | "false" | 1 | 0

let flag:flag = true

或者我们定义一个异常的方法,也可以让方法返回值为never类型

function _error(error_msg:string):never 
    throw new Error(error_msg)


_error("test error")

但是在实际开发过程中,never类型最重要的是一个检查机制,例如在switch多分支选择的时候,如果甲方需要添加新的需求,那么新程序员在补充shit山代码的时候,可能会没有考虑到switch中的分支导致项目错误,这个使用使用never类型的常量进行default的兜底处理就非常重要了,这样就可以在ts编译的时候就发现错误

interface AAA 
    type: "AAA"


interface BBB 
    type: "BBB"


interface CCC 
    type: "CCC"


type ABC = AAA | BBB |CCC
let switch_type = (val:ABC) => 
    switch (val.type) 
        case "AAA":
            break
        case "BBB":
            break
        default:
            const check:never = val
            break
    

在上述代码中,没有考虑到CCC的情况,那么在default中会有一个check的检查报错,可以提醒我们代码有问题!

正常修改后的代码应该是如下:

let switch_type = (val:ABC) => 
    switch (val.type) 
        case "AAA":
            break
        case "BBB":
            break
        case "CCC":
            break
        default:
            const check:never = val
            break
    

2.11 symbol类型

symbol类型是ES6的新特性之一,目的是为了拥有唯一性。

下面列出了几种方法,来测试symbol如何读取

let s: symbol = Symbol("woodwhale")
let s2: symbol = Symbol("woodwhale")

let map = 
    [s]: "value",
    [s2]: "value2",
    name: "woodwhale",
    sex: 1


// foreach 循环,无法读取symbol的键
for (let key in map) 
    console.log(key)

/*
    name
    sex
*/

// 使用Objeck.keys方法获取键,也无法读取symbol的值
console.log(Object.keys(map))
/*
    [ 'name', 'sex' ]
*/

// 使用Object.getOwnPropertyNames方法获取属性名也无法读取symbol
console.log(Object.getOwnPropertyNames(map))
/*
    [ 'name', 'sex' ]
*/

// 使用JSON.stringify,也无法获取symbol
console.log(JSON.stringify(map))
/*
    "name":"woodwhale","sex":1
*/

// 使用Object.getOwnPropertySymbols方法只能获取其中的symbol
console.log(Object.getOwnPropertySymbols(map))
/*
    [ Symbol(woodwhale), Symbol(woodwhale) ]
*/

// 使用Reflect.ownKeys可以获取所有的键,包括symbol
console.log(Reflect.ownKeys(map))
/*
    [ 'name', 'sex', Symbol(woodwhale), Symbol(woodwhale) ]
*/

在Symbol对象中有一个iterator的属性,也叫做迭代器,每一个可以遍历的数组都具有这个性质(自己写的类生成的对象是没有的),迭代器具有next()方法,可以迭代到下一个位置

let _arr:Array<number> = [1,1,4]
let __arr:number[] = [1,33,4,5]

let it = _arr[Symbol.iterator]()

console.log(it.next())
console.log(it.next())
console.log(it.next())
console.log(it.next())

/*
     value: 1, done: false 
     value: 1, done: false 
     value: 4, done: false 
     value: undefined, done: true 
*/

其中value就是当前迭代的值,done表示是否迭代到头了

根据迭代器的性质,写一个迭代方法

function gen(arr: any) 
    let it = arr[Symbol.iterator]()
    let flag = false
    while (!flag) 
        let next = it.next()
        flag = next.done
        if (!flag) console.log(next)
    


gen(new Set([1, 4, 5, 6]))
/*
     value: 1, done: false 
     value: 4, done: false 
     value: 5, done: false 
     value: 6, done: false 
*/

ts的编写者肯定考虑到了上述场景的使用情况,所以直接用了一个of关键字的语法糖来实现迭代生成

for (let item of new Set([1, 4, 5, 6])) 
    console.log(item)

/*
    1
    4
    5
    6
*/

到这里我们会发现 ofin 有什么区别呢,其实 of 是对容器中的值进行迭代,是一个获取值的方式,而 in 是对容器中的键进行遍历,是一个获取索引的方式

3、函数

ts的函数和js差不多,但是多了很多的拓展,这里列出一些比较常用的属性

  • 默认参数函数

    function fun_test(name: string, age: number): void 
        console.log(name + age)
    
    
    // 携带默认参数的方法
    function fun_test_with_default_args(name: string = "woodwhale", age: number = 19): void 
        console.log(name + age)
    
    
    fun_test("woodwhale", 18)
    fun_test_with_default_args()
    /*
        woodwhale18
        woodwhale19
    */
    
  • 方法重载

    // 方法重载,重载的方法需要声明,同时满足不同变量(不同的返回值类型)
    function over_fun_test(params: number): void
    function over_fun_test(params1: number, params2: string): void
    function over_fun_test(params: number, params2?:string): void 
        console.log(params,params2)
    
    
    over_fun_test(1)
    over_fun_test(2,"重载方法")
    /*
        1 undefined
        2 重载方法 
    */
    

4、接口

在typescript中,规范了类和接口的使用,我们先学习接口的使用

使用关键字interface来定义接口,两个名字相同的接口会进行合并的操作,实现某个接口的变量需要完善其接口中的每个定义,否则报错

interface Person    // 接口
    name: string

interface Person   // 两个重名的接口是会合并的
    readonly age?: number    // ? 表示 可选操作符, 表示这个属性在定义的时候可有可无,readonly关键字表示属性是只读的,不可以编辑

interface Person 
    [propName: string]: any //  [propName: string]表示可以自定义填充键,使用any表示值的类型可以是随意的

interface Person 
    fun(): string   // 定义函数

let a: Person = 
    name: "woodwhale",
    age: 19,
    test: ,
    fun: () => 
        return "sheepbotany"
    ,

console.log(a)
/*
     name: 'woodwhale', age: 19, test: , fun: [Function: fun] 
*/

使用extends关键字还可以让接口继承

interface PersonPro extends Person 
    sex: string


let b: PersonPro = 
    name: "sheepbotany",
    age: 21,
    fun() 
        return "woodwhale"
    ,
    sex: "女"

console.log(b)
/*
     name: 'sheepbotany', age: 21, fun: [Function: fun], sex: '女' 
*/

5、类

5.1 简单的例子

在typescript完善了js的面向对象的规范,这里直接举一个最简单的例子,编写一个Person类,具有name、age、sex的属性

class Person 
    name:string
    age:number
    sex:string
    constructor(name:string,age:number,sex:string) 
        this.name = name
        this.age = age
        this.sex = sex
    


let woodwhale = new Person("woodwhale",19,"man")
console.log(woodwhale)
/*
	Person  name: 'woodwhale', age: 19, sex: 'man' 
*/

写完感觉非常像kotlin和java,特别是这个构造函数constructor

但是在typescript也有规定,需要声明属性,如果有没有使用到的属性,需要选择赋予初值

5.2 修饰符

对于每个class的属性,都有属性可以用修饰符修饰

  • public 内部外部都可以访问,同时是默认的
  • private 私有的,只能在内部使用
  • protected 保护的,外部不可访问,内部可以使用,子类可以访问

5.3 父子类

使用关键字extendssuper关键字完成父子类的构建,和Java一样,就不多说了,举一个例子就懂了

class Person 
    name:string
    age:number
    sex:string
    constructor(name:string,age:number,sex:string) 
        this.name = name
        this.age = age
        this.sex = sex
    


class Man extends Person
    constructor(name:string,age:number) 
        super(name,age,"man")
    

5.4 static静态修饰

这个和Java也是一样的,static可以修饰属性,也可以修饰方法

class Person 
    name:string
    age:number
    sex:string
    constructor(name:string,age:number,sex:string) 
        this.name = name
        this.age = age
        this.sex = sex
    以上是关于TSTypeScript语法学习的主要内容,如果未能解决你的问题,请参考以下文章

dart学习-- Dart之基础语法

Dart语法学习-变量

Dart语法学习-变量

Typescript 类型推断、扩展语法和多类型返回

TypeScript 无法在扩展语法函数调用中推断数组类型

go语言学习笔记 — 基础 — 基本语法 — 常量与变量 — 常量变量的声明:多个变量的初始化声明与赋值