ts基础教程(三)

ts基础教程(三)

声明合并

如果定义了两个相同名字的函数(这有些像重载),枚举,接口,或类,那么它们会合并成一个类型

1. js中的同名函数

//如果是js,同名函数后者会覆盖前者

function f1(){
    console.log("f1 top")
}
function f1(){
 console.log("f1 bottom")
}

f1();//f1 bottom

2.1 基于参数个数的函数重载

ts支持函数重载(参数个数/参数类型/参数顺序/返回值,任意一个不同都可形成重载),值得注意的是,需要先声明,后实现


//对于重载的声明
function computed(m: number): number;
function computed(m: number,n:number): number;

//这里是具体实现
function computed(m: number ,n?: number): number {

    if(n){
        return m+n
    }else{
        return 2*m
    }

}
console.log(computed(2))//4
console.log(computed(1,2))//3

2.2 基于参数类型的函数重载

对于不同类型的参数,在实现时候使用管道符|进行分隔

function computed(m: number,n:string): number;
function computed(m: number,n:number): number;

function computed(m: number ,n: number|string): number {

    if(typeof(n)==="string"){
        return +n+m
    }else{
        return m+n
    }

}

console.log(computed(1,2))//3
console.log(computed(1,"2"))//3

2.3 基于参数顺序的函数重载

m,n参数类型的顺序不一致,也可形成重载

function computed(m: number,n:string): number;
function computed(m: string,n:number): number;

function computed(m: number|string ,n: number|string): number {

    if(typeof(m)==="string"){
        m=+m
    }
    if(typeof(n)==="string"){
        n=+n
    }
    return m+n

}

console.log(computed("1",2))//3
console.log(computed(1,"2"))//3

2.4 基于返回值的函数重载

function computed(m: number): number;
function computed(m: number): string;

function computed(m: number): number|string {
    return Math.random()>0.5?m:m.toString()
}

console.log(typeof computed(1))// number or string

3 枚举的声明合并

定义多个同名的枚举类型也会发生声明合并,但是要注意,只有第一个声明的枚举可以省略赋值,后边的不行


enum Language{
    java,
    php,
    node,
}

//这里需要手动赋值
enum Language{
    python=4
} 

//编译后是这样的

"use strict";
var Language;
(function (Language) {
    Language[Language["java"] = 0] = "java";
    Language[Language["php"] = 1] = "php";
    Language[Language["node"] = 2] = "node";
})(Language || (Language = {}));
(function (Language) {
    Language[Language["python"] = 4] = "python";
})(Language || (Language = {}));
//# sourceMappingURL=index.js.map


4 接口的声明合并

interface Box{
    width:string
}

interface Box{
    height:string
}

//等价于
interface Box{
    width:string
    height:string
}


4.1 属性类型不同无法合并

//这两个无法合并,height类型不一致
interface Box{
    height:string
}

interface Box{
    color:string
    height:number
}

4.2 接口中方法的合并,与函数的合并一样

interface Alarm {
    price: number;
    alert(s: string): string;
}
interface Alarm {
    weight: number;
    alert(s: string, n: number): string;
}

//上述写法等价于

interface Alarm {
    price: number;
    weight: number;
    alert(s: string): string;
    alert(s: string, n: number): string;
}
 

5 类的合并与接口的合并规则一致。

补充章节

1. never类型

never类型表示的是那些永不存在的值的类型,常在抛出错误时使用。特殊情况下,变量也可以使用never声明,比如一个永远为空的数组。never是任何类型的子类型,也可以赋值给任何类型,但是反过来不可以。(自身除外)


function error(message: string): never {
    throw new Error(message);
}
const emptyArr: never[] = []

2. 常量枚举

与普通枚举相比,常量枚举多了一些限制:常量枚举会在编译阶段被删除,不可包含计算成员

const enum Language {
    java,
    node,
    php
}

 console.log([Language.java, Language.node, Language.php])

 // 非常量枚举编译后是这样的
 "use strict";
var Language;
(function (Language) {
    Language[Language["java"] = 0] = "java";
    Language[Language["node"] = 1] = "node";
    Language[Language["php"] = 2] = "php";
})(Language || (Language = {}));
console.log([Language.java, Language.node, Language.php]);
//# sourceMappingURL=index.js.map


// 常量枚举编译后是这样的
 "use strict";
console.log([0 /* java */, 1 /* node */, 2 /* php */]);
// 如果tsconfig.json中removeComments属性为true,就是下面这样,删除注释
console.log([0, 1, 2]);
//# sourceMappingURL=index.js.map


// 不可含有计算属性
// 这样会报错
const enum Color {Red, Yellow, Blue = "blue".length};
console.log(Color.Blue);

3 函数类型接口

接口可用于规范对象,而函数也是对象,所以接口也可以用于规范函数,但有些差别


//作为对象方法使用的普通函数
interface User{
    name:string,
    say:(msg:string)=>string
}


//对方法的入参和返回值进行约束的函数类型接口
interface Say{
    (msg:string):string
}

// 所以上边的写法还可以这样

interface User{
    name:string,
    say:Say
}

const u:User={
    name:"tom",
    say:(msg:string):string=>{
        return msg
    }
}

console.log(u.say("hello"))//hello

泛型

泛型是指在定义函数/接口/类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性,这在面向对象语言中很常见。

1. 初识

从概念出发,泛型可以抽象为一个表示某种特定类型的占位符。


//现在有这样一个需求,log函数传入一个数字作为参数,原样返回,代码如下:

function log1(m:number):number{
    return m
}

//现在需求变了,传入的是个字符串,原样返回,代码如下:
function log2(str:string):string{
    return str
}
// 很明显,log1和log2存在高冗余的代码
// 在不削弱ts静态类型检查能力的情况下,泛型是比any更好的选择
//不一定非要用T表示,这只是个占位符,换成ABCD都行
function log<T>(param:T):T{
    return param
}

//注意看这里:log<T>,函数调用的时候我们并没有手动指定类型,但是不会报错
//这是因为ts的类型推论(合理使用该特性会让代码更简洁)
console.log(log("string"))//string
console.log(log(1024))//1024
console.log(log(true))//true

//也可以是这样的

console.log(log<string>("string"))//string
console.log(log<number>(1024))//1024
console.log(log<boolean>(true))//true



2. 感知

泛型可以使用多个,且具有关联性


//定义一个交换函数swap,入参为一个元组,返回值为交换后的元组

function swap<T, U>(tuple: [T, U]): [U, T] {
    return [tuple[1], tuple[0]];
}

console.log(swap(['lengyuexin', '冷月心'])); // ['冷月心', 'lengyuexin']

//关联性指的是所有用同一个占位符(泛型)占位的,在类型确定时都会被统一替换成该类型
//当然,这可能看起来有点像废话...
//手动指定类型 <string,string>,第一个string替换所有的T,第二个string替换所有的U
console.log(swap<string,string>(['lengyuexin', '冷月心'])); // ['冷月心', 'lengyuexin']

3. 不惑

泛型还可以只用某一部分,或者称其为泛型变量


//现在有这样一个需求,输出某个数组的长度
//这样会报错,T身上没有length属性
function getLength<T>(arr:T):number{
    return arr.length
}

//可以更精细一些,既然确定是数组,就将确定部分具体化
//Array<T>,这里泛型就不是完全的占位了,是部分占位
function getLength<T>(arr:Array<T>):number{
    return arr.length
}

// 还可以更稳妥的加入断言,以下两种断言方式都ok

function getLength<T>(arr:Array<T>):number{
    return (<Array<T>>arr).length
}

function getLength<T>(arr:Array<T>):number{
    return (arr as Array<T>).length
}


4 洞玄

补充章节提到了函数类型接口,实际上,在接口中也可以接入泛型


//不接入泛型的函数接口
interface Say{
    (msg:string):string
}

//接入泛型的函数接口

interface Say<T>{
    (msg:T):T
}


//声明函数-此时需要手动指定类型
const say:Say<string>=(msg:string)=>msg

console.log(say("函数接口中的泛型..."))//函数接口中的泛型...


5 知命

泛型也可以用在类中,作用于类的成员属性和方法。

class Print<T> {
  private store: Array<T> = [];

  inner(msg: T) {
    this.store.push(msg);
  }

  outer() {
    console.log(this.store);
  }
}

//使用类型推论可以不限制类型的插入 const p = new Print();
//一旦限定,就只能同类型的插入
const p = new Print<string>();

p.inner("吃饭");
p.inner("睡觉");
p.inner("打豆豆");
// p.inner(1024);
p.outer()//['吃饭','睡觉','打豆豆']


参考链接

  • 掘金ts小册:https://juejin.im/book/5da08714518825520e6bb810
  • ts中文手册:https://zhongsp.gitbooks.io/typescript-handbook
  • ts入门教程:https://ts.xcatliu.com
  • ts常见问题整理:https://juejin.im/post/5e33fcd06fb9a02fc767c427
原创文章 451 获赞 859 访问量 28万+

猜你喜欢

转载自blog.csdn.net/qq_42813491/article/details/104732403