TypeScript基础语法

74 阅读12分钟

前言

image.png

image.png

image.png

image.png

image.png

image.png

image.png

TS的编译环境

image.png

  • 安装命令
    npm install typescript -g
  • 查看版本
    tsc --version

ts入门案例:通过tsc _文件名_,最终生成对应的.js文件。

let message: string = "Hello"

console.log(message);

// TS中的每个文件都是一个独立的模块,如果不加上模块,默认就是在全局环境
// 当我tsc生成js文件时,就会有两个相同的message,那么就会报错
// 写上 export{} 后,就表示这个文件是一个独立的模块
export { }
  • TS中的每个文件都是一个独立的模块,如果不加上模块,默认就是在全局环境
  • 当我tsc生成js文件时,就会有两个相同的message,那么就会报错
  • 写上export{}后,就表示这个文件是一个独立的模块

由于浏览器不认识ts文件,所以需要通过tsc命令生成js代码,这样浏览器才能识别,上述是第一种编译方法

image.png

使用ts-node

  • 安装ts-node
    • npm install ts-node -g
  • 另外ts-node需要依赖 tslib 和 @types/node 两个包:
    • npm install tslib @types/node -g
  • 现在,我们可以直接通过 ts-node 来运行TypeScript的代码:
    • ts-node math.ts

在项目中,我们使用脚手架搭建的时候,webpack就会帮我们配置好对应的loader,所以不用着急。

变量的声明

image.png

不推荐var,推荐let;注意类型不要写成大写的,大写的是类!!!

let message: string = '消息'
const name: string = 'coder'
const age: number = 18

console.log(message, name, age);

export { }

变量的类型推导(推断)

image.png

image.png

  • 声明一个标识符时,如果有直接进行赋值的话,TS会根据赋值的类型,自动推导出标识符的类型注解;
    • 这个过程称之为:类型推导
  • let进行类型推导,推导出来的是通用类型;
  • const进行类型推导,推导出来的是字面量类型(后续会说);

数据类型:

image.png

number类型

数字类型是我们开发中经常使用的类型, TypeScript和JavaScript一样,不区分整数类型(int)和浮点型(double),统一为 number类型

学习过ES6应该知道, ES6新增了二进制和八进制的表示方法,而TypeScript也是支持二进制、八进制、十六进制的表示:

image.png

boolean类型

boolean类型只有两个取值: true和false,非常简单

image.png

string类型

string类型是字符串类型,可以使用单引号或者双引号表示:

image.png

Array类型

数组类型的定义也非常简单,有两种方式:

  • Array<string>事实上是一种泛型的写法,我们会在后续中学习它的用法;

image.png


// 明确的指定数组的类型注解:有2种写法:并且在真实开发中,数组中一般存放相同的类型!!!

// 1. string[] : 数组类型,并且数组中存放的是字符串类型
let names: string[] = ['abc', 'cba', 'nba']

// 2. Array<string> : 数组类,并且指定存放什么类型,这个是泛型的写法,后续会说
let name: Array<string> = ['aaa', 'bbb', 'ccc']

export { }

明确的指定数组的类型注解:有2种写法:并且在真实开发中,数组中一般存放相同的类型!!!

  • string[] : 数组类型,并且数组中存放的是字符串类型;
  • Array<string> : 数组类,并且指定存放什么类型,这个是泛型的写法,后续会说

object类型

image.png

image.png

我们在开发中,会使用type来指定对象类型,在这里我们暂时先用类型推断实现,并且我们是不会用这种写法定义对象类型的:

image.png

因为指定:object表示是一个空对象类型,因此我们通过info.name取值的时候,是取不到的!!!需要我们明确指定类型;

Symbol类型(用的很少)

image.png

null和undefined类型

在 JavaScript 中, undefined 和 null 是两个基本数据类型。

在TypeScript中,它们各自的类型也是undefined和null,也就意味着它们既是实际的值,也是自己的类型:

let n: null = null
let u: undefined = undefined

函数方面的类型

函数的参数类型

函数是JavaScript非常重要的组成部分, TypeScript允许我们指定函数的参数和返回值的类型。

参数的类型注解

  • 声明函数时,可以在每个参数后添加类型注解,以声明函数接受的参数类型:
function sum(num1: number, num2: number) {
    return num1 + num2
}

sum(123, 321)

export { }

如果我们这里参数不指定类型,ts会类型推断吗?

答案是不会!因为函数是被调用的,我们可以传递任意类型的值,可以是字符串、数字,所以这里需要我们手动指定类型;

函数的返回值类型

我们也可以添加返回值的类型注解,这个注解出现在函数列表的后面:

和变量的类型注解一样,我们通常情况下不需要返回类型注解,因为TypeScript会根据 return 返回值推断函数的返回类型:

  • 某些第三方库处于方便理解,会明确指定返回类型,看个人喜好;
function sum(num1: number, num2: number): number {
    return num1 + num2
}

sum(123, 321)

export { }

image.png

匿名函数的参数

匿名函数与函数声明会有一些不同:

  • 当一个函数出现在TypeScript可以确定该函数会被如何调用的地方时;
  • 该函数的参数会自动指定类型;
const names = ['aaa', 'bbb', 'ccc']


// forEach接收函数的3个参数:每一项值、索引、数组本身
// 匿名函数是否需要加类型注解呢?最好不要,因为匿名函数会根据forEach传递过来函数的类型进行推断
names.forEach(function (item, index, arr) {

})

export { }

我们并没有指定item的类型,但是item是一个string类型:

  • 这是因为TypeScript会根据forEach函数的类型以及数组的类型推断出item的类型;
  • 这个过程称之为上下文类型(contextual typing) ,因为函数执行的上下文可以帮助确定参数和返回值的类型;

一般情况下,手动定义的函数,都是没有上下文的,就需要手动指定类型;而匿名函数是传递给别人的,别人在使用匿名函数的时候,会给出数值,TS就会进行推断。

对象类型

如果我们希望限定一个函数接受的参数是一个对象,这个时候要如何限定呢?

  • 这个时候可以使用对象类型;
// 对象类型和函数类型的结合使用
// 传进来一个point对象,该对象里面的参数是x、y并且是number类型,并且一定可以取到

function printCoordinate(point: { x: number, y: number }) {
    console.log("x坐标:", point.x);
    console.log("y坐标:", point.y);
}

printCoordinate({ x: 30, y: 40 })

export { }

但是我们一般使用type来定义对象类型:

type PointType = { x: number, y: number }
function printCoordinate(point: PointType) {
    console.log("x坐标:", point.x);
    console.log("y坐标:", point.y);
}

在这里我们使用了一个对象来作为类型:

  • 在对象我们可以添加属性,并且告知TypeScript该属性需要是什么类型;
  • 属性之间可以使用,或者;来分割,最后一个分隔符是可选的:(point: { x: number;y: number })
  • 每个属性的类型部分也是可选的,如果不指定,那么就是any类型;

可选类型

对象类型也可以指定哪些属性是可选的,可以在属性的后面添加一个?

type PointType = { x: number, y: number, z?: number }
function printCoordinate(point: PointType) {
    console.log("x坐标:", point.x);
    console.log("y坐标:", point.y);
}

printCoordinate({ x: 30, y: 40 })
printCoordinate({ x: 10, y: 20, z: 30 })

export { }

调用printCoordinate,这两个都不会报错,因为z设置了可选类型,可以传,也可以不传;

TypeScript扩展类型

any类型

在某些情况下,我们确实无法确定一个变量的类型, 并且可能它会发生一些变化,这个时候我们可以使用any类型(类似于Dart语言中的dynamic类型)。

any类型有点像一种讨巧的TypeScript手段:

  • 我们可以对any类型的变量进行任何的操作,包括获取不存在的属性、方法;
  • 我们给一个any类型的变量赋值任何的值,比如数字、字符串的值;
let id: any = 123

id = "aaaaa"

id = {
    name: '哈哈',
    age: 123
}

const infos: any[] = [123, "abc", {}]

如果对于某些情况的处理过于繁琐不希望添加规定的类型注解,或者在引入一些第三方库时,缺失了类型注解,这个时候我们可以使用any:

  • 包括在Vue源码中,也会使用到any来进行某些类型的适配;

unknown类型

unknown是TypeScript中比较特殊的一种类型,它用于描述类型不确定的变量。

  • 和any类型有点类似,但是unknown类型的值上做任何事情都是不合法的;

image.png

可以给unknown类型赋任何类型的值,但是不可以对他进行操作,比如不能取长度,因为我不知道你是什么类型,我怎么知道你有没有length这个属性?

他的应用场景是:必须进行类型缩小(校验类型),就可以使用了

let foo: unknown = 'aaa'

// 必须要校验类型,typeof实现:类型缩小;
if (typeof foo === "string") {
    console.log(foo.length);
}

image.png

void类型

void通常用来指定一个函数是没有返回值的,那么它的返回值就是void类型:

function sum(n1: number, n2: number) {
    // 没有return返回,而是直接log打印,那么默认就是void类型
    console.log(n1 + n2);
}

function sum(n1: number, n2: number):void {
    console.log(n1 + n2);
    return undefined;
}

如果返回值是void类型,那么我们也可以返回undefined,TS编译器是允许我们这样做的。

这个函数我们没有写任何类型,那么它默认返回值的类型就是void的,我们也可以显示的来指定返回值是void:

定义函数类型的时候,需要使用=>,某些时候,使用:

type LyricInfoType = {time:number,text:string}

function parseLyric(lyric:string):LyricInfoType[]{
    const lyricInfos:LyricInfoType[] = []
    
    return lyricInfos
}

export {}

实际上,parseLyric函数,本质上也是一个对象(因为函数和对象具有相同特性),它本身也是有类型的:

应用场景:用来指定函数类型的返回值是void;

const name: stirng = "aaa"

const foo = () => {
    
}

-----------------------

const foo:()=>void = () =>{
}

这样看会更直观一点:foo函数是一个没有参数、 没有返回值的函数,然后这个函数返回的是一个无参箭头函数;这种代码阅读性非常的差,我们会通过类型别名来实现:

type FooType =() => void
const foo: FooType =() =>{
    ...
}

应用场景:定义一个要求传入函数类型时,会使用到;

这里指定delay的参数,接收的必须是一个函数:

function delay(fn:()=>void){
    fn()
}

delay(()=>{
    
})

同时也可以接收多个函数作为参数type xxx = (...args:any[])=>void

type ExecFnType = (...args: any[]) => void

// 定义一个函数,并且接收的参数也是一个函数,而且这个函数的类型必须是ExecFnType
function delayExecFn(fn:ExecFnType){
    fn("why",18)
}

// 执行上面函数,并且传入一个匿名函数
delayExecFn((name,age)=>{

})

总结:也就是说,void用来指定是一个函数类型;在某些参数中需要传入函数时,会用到!

image.png

在进行类型推导的时候,如果很不巧,TS推导出这个函数的返回值是一个void,虽然明面上好像不可以返回内容,但是实际上,TS不会强制函数,一定不可以返回,还是可以返回的(不建议),但是仅限于类型推导出返回值是void;

never类型

开发中很少实际的去定义never类型,某些情况下会自动进行类型推导,得出never类型;

never 表示永远不会发生值的类型,比如一个函数:

  • 如果一个函数中是一个死循环或者抛出一个异常,那么这个函数会返回东西吗?
  • 不会,那么写void类型或者其他类型作为返回值类型都不合适,我们就可以使用never类型;
// 1.抛异常
function foo(): never {
    throw new Error("111");
}
foo()

// 2.永远不会发生值
function parseLyric() {
    return []
}

never有什么样的应用场景呢?

  • 这里我们举一个例子,但是它用到了联合类型,后面我们会讲到:
  • 以及开发框架的时候或者封装工具的时候,都是会用到never的。
function handleMessage(message: string | number) {
    switch (typeof message) {
        case "string":
            console.log(message.length);
            break
        case "number":
            console.log(message);
            break
        default:
            const check = message
    }
}

实际上default中的语句,是永远不会被执行到的,因为一旦不是string或number类型,就根本不会执行这个函数;那么此时这个check就是never类型。

这里如果不实现boolean的case的话,message的值就会被赋值,但是never的意思是不可能有值的,所以在其他时候在扩展工具时,对于一些没有处理的case,此时使用never就可以提醒开发人员,书写对应的逻辑代码,不实现逻辑则会报错。

function handleMessage(message: string | number | boolean) {
    switch (typeof message) {
        case "string":
            console.log(message.length);
            break
        case "number":
            console.log(message);
            break
        default:
            const check:never = message
    }
}

tuple类型

tuple是元组类型,很多语言中也有这种数据类型,比如Python、 Swift等

那么tuple和数组有什么区别呢?

  • 首先, 数组中通常建议存放相同类型的元素, 不同类型的元素是不推荐放在数组中。(可以放在对象或者元组中)
  • 其次, 元组中每个元素都有自己特性的类型,根据索引值获取到的值可以确定对应的类型;

元组中每个索引的值都有特定的类型:

// 数组:
const info1: any[] = ['why', 18]

// 使用对象类型(用的最多)
const info2 = {
    name: 'why',
    age: 18
}

// 元组:我能百分百确定第一项的类型是string,第二项的类型是number
const tInfo: [string, number] = ['why', 18]

元组数据结构中,可以存放不同的数据类型,取出来的item也是具有明确数据类型的。

那么tuple在什么地方使用的是最多的呢?

  • tuple通常可以作为返回的值,在使用的时候会非常的方便;
function useState(initialState: number): [number, (newValue: number) => void] {
    let stateValue = initialState
    function setValue(newValue: number) {
        stateValue = newValue
    }

    return [stateValue, setValue];
}

const [count, setCount] = useState(10)

export { }

指定这个函数的返回值是一个元组,并且第一项是number类型,第二项是一个函数,并且这个函数还有一个参数,类型是number类型。当我们使用useState方法时,我们就会知道count是一个number类型,而setCount是一个具有number参数的函数。