前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >typescript基础笔记

typescript基础笔记

作者头像
wade
发布2022-03-28 18:49:24
6850
发布2022-03-28 18:49:24
举报
文章被收录于专栏:coding个人笔记coding个人笔记

typescript早在2013年就发布了第一个正式版本,印象中一直到了19年才大火起来。三年过去了,一直是可用可不用的状态,于是很多人都没学习使用。直到react和vue开始捆版上了ts,前端圈也开始了“内卷”,ts已经是不得不用的状态了。

这次分享的是自己学习过程觉得掌握了就可以上手的内容,上手了之后通过项目多实践, 实践过程再学习深入的内容,应该就能比较快的掌握。

学习过程贴的代码都是在在线的这个平台的演练场调试的:https://www.typescriptlang.org/zh/

tips:
  1. ts最终都会编译成js,添加的类型最终都会被删除,只是为了开发的时候提示
  2. ts一切类型校验的目标都是为了安全
  3. ts冒号(:)后面的都是类型
  4. ts中小写的(string)叫类型,描述基础类型,大写的(String)是类,描述的是实例
一、基础类型
boolean、number、string
代码语言:javascript
复制
let num: number = 3;
let have: boolean = true;
let str: string = 'str';
null、undefined

null和undefined对应的类型就是本身。不开启严格模式的时候可以赋值给任何类型, 也就是任何类型的子类型。一般会开启,所以null和undefined只能是自己的类型。

strictNullChecks:严格检测null undefined

代码语言:javascript
复制
let nu: null = null;
let un: undefined = undefined;
any

ts中所有的类型都可以是any类型,使用any类型也意味着放弃类型校验。除非特殊情况,并不建议使用。

unknown

为了解决any带来的问题所出现的类型,也是所有类型都可以是unknown类型。unknown类型被当作安全的类型。

unknown只能赋值给unknown或者any:

代码语言:javascript
复制
let a: unknown = 1;
let b: any = a;
let c: number = a;//err

unknown类型不能进行运算、调用属性、当作函数:

代码语言:javascript
复制
let a: unknown;
a.toString();//err
a();//err
let b = a + 1;//err

unknow使用的时候要把类型具体化,缩小使用范围:

代码语言:javascript
复制
function isString(val: unknown): void{
    if(typeof val === 'string'){
        val.toString();
        val.toFixed();//err
    }
}

unknown会被当作安全类型的原因是不能进行运算、调用属性、当作函数, 使用的时候类型要具体化,缩小使用范围,这样就可以避免any的那些不安全的副作用。

void

void类型只是当作函数的没有返回值的时候使用,表示没有任何类型。函数不加void类型, 默认返回void,所以如果函数没有返回值的时候void加不加感觉都可以。

Array

ts的数组有两种写法,常规写法:

代码语言:javascript
复制
let arr1: string[] = [''];
let arr2: (string | number)[] = ['', 3];

另外一种泛型写法,会比较少用,把[]变成Array,类型写到尖括号里面:

代码语言:javascript
复制
let arr1: Array<string> = [''];
let arr2: Array<string | number> = [''];

一般开放会用到数组对象([{}]),声明一般两种方法:

代码语言:javascript
复制
interface ListItem{
    name: string
    id: number
    label: string
}
let GroupList1:ListItem[] = [{
    name: '',
    id: 1,
    label: ''
}]
let GroupList2:Array<ListItem> = [{
    name: '',
    id: 2,
    label: ''
}]
Tuple

元组,ts衍生的一种类型,限制数组的长度和顺序:

代码语言:javascript
复制
let tuple:[string, number, boolean] = ['str', 3, true];

元组已知数组的长度和每个子元素的类型,不能多也不能少。可以push已知类型的子元素,但是无法使用。

Enum

ts的枚举只是为了清晰地表达意图或创建一组有区别的用例。

数字枚举:

代码语言:javascript
复制
enum Direction {
    Up = 1,
    Down,
    Left,
    Right
}
console.log(Direction.Up);//0
console.log(Direction[0]);//Up

数字枚举会自动增长,如果第一个不初始化赋值会从0开始。枚举可以通过名字和下标访问,枚举值和枚举名字互相映射。编译后的代码:

代码语言:javascript
复制
"use strict";
var Direction;
(function (Direction) {
    Direction[Direction["Up"] = 0] = "Up";
    Direction[Direction["Down"] = 1] = "Down";
    Direction[Direction["Left"] = 2] = "Left";
    Direction[Direction["Right"] = 3] = "Right";
})(Direction || (Direction = {}));

字符串枚举:

代码语言:javascript
复制
enum Direction {
    Up = "UP",
    Down = "DOWN",
    Left = "LEFT",
    Right = "RIGHT",
}

字符串枚举获取值的方式不能使用下标。

异构枚举: 数字和字符串混合使用,几乎不会使用,因为没什么意义:

代码语言:javascript
复制
enum Direction {
    Up = "Up",
    Down = "DOWN",
    Left = 1,
    Right,
}
object

object表示的是非原始数据类型, 也就是除number,string,boolean,symbol,null或undefined之外的类型。

代码语言:javascript
复制
let a: object = [3, 'str'];
let b: object = {obj: 3};
let c: object = function(){}
never

never意味着永远达不到,函数返回值使用,报错、死循环可以做到, 通常用来校验代码完整性,实现类型的绝对安全,一般很少做这么严格的校验。

二、断言

ts的断言其实就是手动断定是什么类型,主要是为了欺骗编辑器,只在编译阶段起作用, 编译之后断言就移除了,所以使用的时候一定要注意自己断言的结果。

非空断言!

断定某个变量不是空的,也就是不会是undefined或者null:

代码语言:javascript
复制
let a: number;
a+=1;//err

let b: number;
b!+=1;

function setName(name: string | undefined){
    let myName1: string = name;//err
    let myName2: string = name!;
}
as语法

把某个类型断定成某个类型:

代码语言:javascript
复制
function getVal(obj: string | number){
    (obj as number).toFixed();
}

上面的obj可能是string可能是number,string没有toFixed,这时候用as语法, 表示确定obj一定是number。

三、类型保护(类型守卫)

类型保护是运行时的一种检测,当一个变量不确定是什么类型的时候,可以调用共有的属性和方法, 特有的属性和方法就需要配合类型保护。

js提供typeof 、instanceof、 in
代码语言:javascript
复制
//typeof
function getVal(obj: string | number){
    if(typeof obj === 'number'){
        obj.toFixed();
    }
}
//in
type Obj1 = {
    a: number
}
type Obj2 = {
    b: number
}
function getTh(obj: Obj1 | Obj2){
    if('a' in obj){
        console.log(obj.a)
    }
}
//instanceof
type Fn = () => {}
function run(fn: Fn | number){
    if(fn instanceof Function){
        fn();
    }
}
自定义

除了js提供的,ts也可以自定义类型保护,使用is关键字, is关键字一般用于函数返回值类型中,判断参数是否属于某一类型,并根据结果返回对应的布尔类型:

代码语言:javascript
复制
function isObject(val: any): val is Object{
    return Object.prototype.toString.call(val) === '[Object object]'
}

type Dog = {
    type: 'dog'
    eat: () => {}
}
type Cat = {
    type: 'cat'
    speak: () => {}
}
//如果没有animal is Dog,下面调用animal.eat()是会报错,ts中,直接返回true或者false并不能判断是Dog还是Cat
function getIs(animal: Dog | Cat): animal is Dog{
    return animal.type === 'dog';
}
function isDog(animal: Dog | Cat){
    if(getIs(animal)){
        animal.eat();
    }
}

四、联合类型和交叉类型

联合类型其实就是用|组合,交叉类型用&:

代码语言:javascript
复制
//联合类型
let a: string | number;

type A = string | number;
type B = string | boolean;
//交叉类型
let b: A & B = 'str';

交叉类型如果是基本数据类型,合并的时候同名会出现never:

代码语言:javascript
复制
type Obj1 = {
    a: string
    b: number
}
type Obj2 = {
    a: number
    c: string
}
type Obj = Obj1 & Obj2;

Obj相当于:

代码语言:javascript
复制
type Obj = {
    a: never;
    b: number;
    c: string;
}

出现never其实就是报错,一般不会出现。

五、接口interface和类型别名type

接口和类型别名在ts中非常重要,这两个在一般情况可以通用,也有区别。一定要记住,声明interface和type的时候用类型,实现的时候用具体的值。

接口interface

接口用来描述数据的形状,没有具体的实现(抽象的),接口可以描述函数、对象、类。(语法上分号和逗号可写可不写)

描述对象:

代码语言:javascript
复制
interface Person{
    age: number
    name: string
}

可选、只读属性:

代码语言:javascript
复制
interface Person{
    readonly age: number
    name?: string
}

一般接口定义的属性一定要实现,修饰符?表示可选,函数参数也是这样使用。只读是实现的时候初始话可以赋值,之后赋值就会报错。

任意属性:

代码语言:javascript
复制
interface Person{
    age: number
    name: string
    [other: string]: any
}

语法就是key用[]包裹,key用什么类型,值一般用any,否则已经定义的和这边的都要统一一个类型。

扩展属性:

接口定义好之后,想要扩展属性,一般使用继承或者自动合并:

代码语言:javascript
复制
interface Person{
    age: number
    name: string
}
//自动合并
interface Person{
    sex: string
}
//继承
interface Son extends Person{
    sex: string
}

其它方法用as断言、使用ts的兼容性几乎不用。

类型别名type

类型别名也可以用来描述对象和函数,还可以描述原始类型、元组、联合类型等。语法上面类型别名是=。

代码语言:javascript
复制
type Person = {name: string, age: number};
type Name = string;
type Arr = [string, number];
type Uni = Person | Arr;
接口和类型别名的区别

接口和类型别名很多时候会分不清,因为用起来一个样,把区别理清楚了就知道两者的通用和不同。

函数语法区别较大:

代码语言:javascript
复制
type Fn1 = (a: string) => number
interface Fn2{
    (a:string): number
}
let fn: Fn2 = (a: string) => {
    return 5
}

接口还是对象形式,参数:返回,类型别名则直接是箭头函数。

type可以使用联合类型和具体的值和元组:

代码语言:javascript
复制
interface Obj{
    a: string
}
type Tu = [string];
type A = 1;
type Uni1 = Tu | A | Obj;
type Uni2 = Tu & A & Obj;

type的联合类型可以联合接口,使用联合类型就相当于扩展type,没办法扩展自身。

接口可以继承和多继承,接口还可以继承类型别名,会自动合并,可扩展:

代码语言:javascript
复制
type Obj1 = {a: string}
interface Obj2{
    b: string
}
interface Obj2{
    c: string
}
interface Obj3 extends Obj1, Obj2{
    d: string
}

type可以内部使用in语法,interface无法使用in语法:

代码语言:javascript
复制
type Obj1 = {a: string, b: number}
type Obj2<T> = {[K in keyof T]: T[K]}

这个在后面泛型使用的时候很有用。

类可以实现接口或类型别名,但类不能实现联合类型的别名:

代码语言:javascript
复制
interface Obj1{
    a: string
}
type Obj2 = {b: string};
type Obj3 = Obj1 | Obj2;
class O1 implements Obj1{
    a = 'a'
}
class O2 implements Obj2{
    b = 'b'
}
//err
class O3 implements Obj3{
    b = 'b'
}

六、泛型

泛型在ts中非常重要,使用泛型来创建可重用的组件,一个组件可以支持多种类型的数据。这样用户就可以以自己的数据类型来使用组件。在定义类型的时候还不能确定是什么类型,在使用的时候才能确定类型。根据传入的类型决定类型。

语法:
代码语言:javascript
复制
function fn<T, K>(a: T, b: K): K{
    return b
}

尖括号里面相当于参数,可以是任意的名字,一般使用:

  • T:type
  • K:key
  • V:value
  • E:element 泛型是类型,并不是具体的参数。

调用的时候如果不具体类型,会根据参数推论出:

代码语言:javascript
复制
fn(1, '');
//不常用
fn<number, string>(1, '');
接口、类型别名泛型:
代码语言:javascript
复制
interface Eat<T>{
    (a: T): T
}
let eat: Eat<string> = () => 'a';
eat('');

interface Food<T>{
    name: T
}
let per: Eat<Food<string>> = (a) => a;
per({name: 'w'})

type Speak<T> = (a: T) => T;
let speak: Speak<string> = (a) => a;
speak('');
泛型约束extends

用来约束泛型的范围,约束要满足约束的特点,满足是拥有特性,只要含有要求的属性

代码语言:javascript
复制
interface Ex{
    name: string
}
function fn<T extends Ex>(obj: T){

}
fn({name: '', age: 3})

这边是只要有name属性就可以,如果我返回值设置T,是不行的:

代码语言:javascript
复制
interface Ex{
    name: string
}
function fn<T extends Ex>(obj: T): T{
    return T //err
}
fn({name: '', age: 3})

T只是约束了具有name属性,但是不能保证T一定是原来的T,可以增删属性。

typeof、keyof、in

typeof:可以用来获取一个变量声明或对象的类型

代码语言:javascript
复制
interface Person{
    name: string
    age: number
}
let person: Person = {name: '', age: 3}
//等价
type PersonS = typeof person;
type PersonO = Person;

keyof:获取某种类型的所有键,其返回类型是联合类型

代码语言:javascript
复制
interface Obj{
    name: string
    age: number
}
type Obj1 = keyof Obj;//"name" | "age"
let obj1: Obj1 = 'name';
let obj2: Obj1 = 'age';

in:用来遍历,看结果是遍历联合类型

代码语言:javascript
复制
type keys = 'name' | 'age';
type Obj1 = {
    [K in keys]: any
};
let obj1: Obj1 = {name: '', age: 3};
条件类型分发

泛型中如果通过条件判断返回不同的类型,放入的是联合类型(|),具备分发功能:

代码语言:javascript
复制
type Obj1 = {name: string};
type Obj2 = {age: number};
type JudgeObj1<T extends Obj1 | Obj2> = T extends Obj1 ? Obj1 : Obj2;
type IsObj1 = JudgeObj1<Obj1>//{name: string}

分发的理解就是,T会先跟Obj1判断,再和Obj2判断,而不是Obj1 | Obj2看成一个整体。

通过内置类型Exclude可以更好的理解(删除第二个参数存在的类型):

代码语言:javascript
复制
//type Exclude<T, U> = T extends U ? never : T
type Ex = Exclude<number | string | boolean, number>
  • number extends string ? never : number得到number
  • string extends string ? never : number得到never
  • boolean extends string ? never : number得到boolean
  • 所以最终结果是string | boolean。会分别把没一个类型去校验。
内置类型、infer

内置类型和infer可以后面好好了解,这边列举几个常用的内置类型

  • Readonly:只读
  • Exclude:删除第二个参数存在的类型
  • Extract:返回符合第二个参数的类型
  • Required:全部变成必填
  • Partial:让所有属性都变成可选
  • NonNullable:去除null和undefined
  • Pick:在对象中挑选
  • Omit:忽略对象中的

.d.ts

ts会检测根目录下所有.d.ts文件,里面用declare声明的都是全局的。declare声明的都没有具体的实现,.d.ts只是为了使代码不报错,没有任何实际功能。

比如用script引入jq,直接$()会报错,在.d.ts声明:

代码语言:javascript
复制
declare function $(){}

ts还有很多需要学习,这些只是简单的了解,然后在开发过程中再慢慢学习其它内容。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-03-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 coding个人笔记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • tips:
  • 一、基础类型
    • boolean、number、string
      • null、undefined
        • any
        • unknown
        • void
        • Array
        • Tuple
        • Enum
        • object
        • never
        • 二、断言
          • 非空断言!
            • as语法
            • 三、类型保护(类型守卫)
              • js提供typeof 、instanceof、 in
                • 自定义
                • 四、联合类型和交叉类型
                • 五、接口interface和类型别名type
                  • 接口interface
                    • 类型别名type
                      • 接口和类型别名的区别
                      • 六、泛型
                        • 语法:
                          • 接口、类型别名泛型:
                            • 泛型约束extends
                              • typeof、keyof、in
                                • 条件类型分发
                                  • 内置类型、infer
                                  • .d.ts
                                  领券
                                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档