使用TypeScript的一些注意事项(回顾总结)

易混乱的类型

never vs void

只需要记住一个特点:返回never的函数 都必须存在无法到达的终点。如死循环、抛出异常。

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

function fn2():never{
    throw new Error()
}

any vs unknown

- any 任何类型  会忽略语法检查
- unknown 不可预知的类型,不会忽略语法检查(这就是最大的区别)

const bar:any = 10
any.substr(1) //any 会忽略所有类型检查

const foo:unknown = "string"
foo.substr(1) //error 语法检查不通过

(foo as string).substr(1) //ok
if(typeof foo === 'string'){foo.substr(1)} //ok
@ts-ignore

增加@ts-ignore 的注释 会忽略下一行的语法检查
const num1:number = 100
num1.substr() //Error语法检查错误

const num2:number = 200
//@ts-ignore
num2.substr() //ok 语法检查通过

类型断言 as

ts只管编译时,不管运行时。as 就是典型的例子。
你用as 告诉编译器类型,编译器就听你的。但运行时 后果自负

function fn(a:string|null){
    const length = (a as string).length
    console.log(length);
}
fn('abc')//ok
fn(null) // Error js运行报错
非空断言操作符!

!用于排除null undefined,即告诉编译器:xx变量肯定不是null或undefined.
同理,运行时有可能出错。
//例子 1
function fn(a:string|null|undefined){
let s:string=""
s = a//Error 语法检查失败
s = a!//OK --如果a 真的是null或者undefined,那么s也会是null或者undefined,
//可能会带来bug
}
//fn(null)

//例子 2
type NumGenerator = ()=>number;
function myFunc(numGenerator:NumGenerator|undefined){
const num1 = numGenerator() //Error 语法检查失败
const num2 = numGenerator!() //OK
}
// myFunc(undefined) //如果真的传入undefined,也会去执行,当然执行会报错

// 例子3
let a:number
console.log(a);//Error -Variable “n” is used before being assigned
let b!:number
console.log(b);//ok !表示 你会给b一个赋值 不用编译器关心

type 和 interface

如果要定义一个静态的类型,仅有一些属性,没有方法 就用type
如果定义一种行为(行为肯定需要使用方法,仅属性是不够的),需要class实现,就用interface

可选链?.

?.遇到null或undefined就可以立即停止某些表达式的运行,并返回undefined,但是这里只针对null
和undefiend,对于0 false ""等falsely变量是不起作用的,这一点和&&不一样

这个运算符,看似是获取一个属性 其实它是有条件判断的。
即,它就是一个?: 三元表达式的语法糖。既然它有逻辑判断,那你考虑不到位,就有可能出错。

// 例子1-获取对象属性
interface IFoo{a:number}
function fn (obj:IFoo|null|undefined):number|undefined{
    const a = obj?.a//?.可选链运算符
    //第一 如果a是IFoo类型  则打印100
    // 第二  如果a是null或者undefined  则打印undefined
    console.log('a',a);
    return a //100 或者undefined
}
fn({a:100})
// fn(null)
// fn(undefined)




// 例子二  获取数组元素

function tryGetArrayElement<T>(arr?:T[],index:number = 0){
    return arr?.[index]
}
//编译产出:
// "use strict";
// function tryGetArrayElement(arr,index=0){
//     return arr === null ||arr===void 0?void 0:arr[index]
// }




// 例子三:用于函数调用
type NumGenerator = ()=>number
function fn(numGenerator:NumGenerator|undefined|null){
    const num = numGenerator?.();
    console.log('num',num)//如果不是函数 则不调用,也不会报错 返回undefined
}

fn(null)
fn(undefined)

private 和 ‘#’

两者都表示私有属性。背景不同:
private 是ts中一开始就有的语法,而且目前只有ts有, ES规范 没有。
‘#’ 是ES目前的语法提案,然后被ts 3.8支持了,即ts和es都支持#

如果仅对于ts来说 用哪个都一样。
但是:ts只关注编译时,不关注运行时。所以,还得看两者的编译结果

private

private 编译之后就失去了私有的特点。即,如果你执行了(new Person()).name,虽然语法检查不通过,但是运行时是可以成功的。
即private仅仅是ts的语法 编译成js之后,就失效了。

class Person{
    private name:string;
    constructor(){
        this.name = "zhangsan"
    }
}

// 编译结果如下
"use strict";
class Person {
    constructor(){
        this.name = "zhangsan"
    }
}
‘#’

'#'编译之后,依然具有稀有特点 而且用(new Person()).name,在运行时也是无法实现的,即 “#”是ts语法 但同时也是Es的提案语法,编译之后也不能失效
但是 编译结果中,私有是通过WeekMap来实现的 所以要确保你的运行环境支持ES6。WeekMap没有完美的Polyfill方案 强行Polyfill可能会发生内存泄漏

// ts 源码
class Person{
    #name:string
    constructor(){
        this.#name = "zhangsan"
    }
}

// 编译结果如下
// "use strict"
// var __classPrivateFieldSet = (this&&this.__classPrivateFieldSet)||function(receiver,privateMap,value){
//     if(!privateMap.has(receiver)){
//         throw new TypeError('attempted to set private filed on non-instance')
//     }
//     privateMap.set(receiver,value);
//     return value
// };

// var _name;
// class Person{
//     constructor(){
//         _name.set(this,void 0)
//         __classPrivateFieldSet(this,_name,'zhangsan')
//     }
// }
// _name = new WeakMap()

ts中的函数重载

ts的函数重载,先把各个情况的函数头写出来,然后再写一个统一的、兼容上述所有情况的函数头。最后,函数体自行处理参数

class Person{
//第一,各个情况的函数头写出来
test():void
test(a:number,b:number):number
test(a:string,b:string):string
//第二。统一的,兼容上述所有情况的函数头(有一个不兼容,就报错)
test(a?:string|number,b?:string|number):void|string|number{
//第三 函数体自行处理参数
if(typeof a === “string” && typeof b === “string”){
return ‘string params’
}
if(typeof a === ‘number’ && typeof b === ‘number’){
return ‘number params’
}
console.log(‘no params’);

}

}

注意函数定义的顺序

参数越精准的,放在前面

// 错误:any 类型不精准  应该放到最后
declare function fn(x:any):any;
declare function fn(x:HTMLElement):number;
declare function fn(x:HTMLDivElement):string;
var myElem:HTMLDivElement;
var x = fn(myElem) // x:any,wat?

不要为仅在末尾参数不同时写不同的重载,应该尽可能使用可选参数

// 错误
interface Example1 {
    diff(one:string):number;
    diff(one:string,two:string):number;
    diff(one:string,two:string,three:boolean):number;
}

// OK
interface Example2{
    diff(one:string,two?:string,three?:boolean):number
}

能熟练使用 ts 已经是一名前端人员必备的技能

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值