ts的高级类型

lxf2023-05-13 01:17:55

1.交叉类型(并集)

交叉类型是将多个类型合并为一个类型。 这让我们可以把现有的多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性。

function extendFn<T, U>(first: T, second: U) {
    let result = <T & U>{};
    for (let key in first) {
        (<any>result)[key] = (<any>first)[key];
    }
    for (let key in second) {
        if (!result.hasOwnProperty(key)) {
            (<any>result)[key] = (<any>second)[key];
        }
    }
    return result
}

const first = {
    name:'bwf'
}
const second = {
    age:18
}
const res1 = extendFn(first,second)
console.log('res',res1);

2.联合类型

联合类型表示一个值可以是几种类型之一。 我们用竖线( |)分隔每个类型,所以 number | string | boolean表示一个值可以是 number, string,或 boolean。

// 需求:给一个值的左侧加上一定数量的空格
function padLeft(txt: string, padding: string | number) {
    if (typeof padding === "string") {
        return padding + txt
    } else if (typeof padding === "number") {
        return Array(padding + 1).join(" ") + txt
    } else {
        throw new Error(`Expected string or number, got '${padding}' is ${typeof padding}`)
    }

}

const indentedString = padLeft('hello world', 5)
console.log('indentedString', indentedString)

如果一个值是联合类型,我们只能访问此联合类型的所有类型里共有的成员。

interface Bird {
   fly();
   layEggs();
}

interface Fish {
   swim();
   layEggs();

}

function getSmallPet():Fish | Bird{
   return 

}
let pet = getSmallPet();
pet.layEggs()  // okay
pet.swim()  //errors : 类型“Bird | Fish”上不存在属性“swim”。类型“Bird”上不存在属性“swim”

3.联合类型的类型保护

上面例子报错如何让它正常运行呢?

类型断言;

let pet = getSmallPet();

if ((<Fish>pet).swim) {
    (<Fish>pet).swim();
}
else {
    (<Bird>pet).fly();
}

用户自定义的类型保护

类型保护就是一些表达式,它们会在运行时检查以确保在某个作用域里的类型。 要定义一个类型保护,我们只要简单地定义一个函数,它的返回值是一个 类型谓词

function isFish(pet: Fish | Bird): pet is Fish {
    return (<Fish>pet).swim !== undefined; 
}

在这个例子里, pet is Fish就是类型谓词。 谓词为 parameterName is Type这种形式, parameterName必须是来自于当前函数签名里的一个参数名。

每当使用一些变量调用 isFish时,TypeScript会将变量缩减为那个具体的类型,只要这个类型与变量的原始类型是兼容的。

// 'swim' 和 'fly' 调用都没有问题了
if (isFish(pet)) { 
    pet.swim(); 
} 
else{ 
    pet.fly(); 
}

typeof 类型保护

见第一个例子。 这些* typeof类型保护*只有两种形式能被识别: typeof v === "typename"和 typeof v !== "typename", "typename"必须是 "number", "string", "boolean"或 "symbol"。 但是TypeScript并不会阻止你与其它字符串比较,语言不会把那些表达式识别为类型保护。

instanceof类型保护

左侧的值是否是右侧值的实例instanceof的右侧要求是一个构造函数,TypeScript将细化为:

  1. 此构造函数的 prototype属性的类型,如果它的类型不为 any的话
  2. 构造签名所返回的类型的联合

4. 可以为null的类型

TypeScript具有两种特殊的类型, null和 undefined,它们分别具有值null和undefined. 默认情况下,类型检查器认为 null与 undefined可以赋值给任何类型。 null与 undefined是所有其它类型的一个有效值。 这也意味着,你阻止不了将它们赋值给其它类型,就算是你想要阻止这种情况也不行。 null的发明者,Tony Hoare,称它为 价值亿万美金的错误。 --strictNullChecks标记可以解决此错误:当你声明一个变量时,它不会自动地包含 null或 undefined。 你可以使用联合类型明确的包含它们 vue3+ts框架默认开启了这一配置

5. ?和 !

? 代表可选参数和可选属性,即值可能为undefined ,如果赋值为null也会报错

function f(x: number, y?: number) { 
    return x + (y || 0); 
} 
f(1, 2); 
f(1); 
f(1, undefined); 
f(1, null); // error, 'null' is not assignable to 'number | undefined'

!表示该值不会是null或undefined

function broken(name: string | null): string {
  function postfix(epithet: string) {
      return name.charAt(0) + '.  the ' + epithet; // error, 'name' is possibly null
  }
  name = name || "Bob";
  return postfix("great");
}

6.类型别名 type

类型别名不会新建一个类型,它创建了一个新 名字来引用那个类型。类型别名有时和接口很像,但是可以作用于原始值,联合类型,元组以及其它任何你需要手写的类型。

7.类型别名 vs 接口 (TODO:

相同点:

一:都可以用于定义类型

二:同接口一样,类型别名也可以是泛型 - 我们可以添加类型参数并且在别名声明的右侧传入:

type Container<T> = { value: T };

不同点:

其一,接口创建了一个新的名字,可以在其它任何地方使用。 类型别名并不创建新名字—比如,错误信息就不会使用别名。 另一个重要区别是类型别名不能被 extends和 implements(自己也不能 extends和 implements其它类型)。 因为 软件中的对象应该对于扩展是开放的,但是对于修改是封闭的,你应该尽量去使用接口代替类型别名。 另一方面,如果你无法通过接口来描述一个类型并且需要使用联合类型或元组类型,这时通常会使用类型别名。

Record<string,string>与{[key:string]:string}相同。只有当该类型的所有属性都已知并且可以对照该索引签名进行检查时,才允许将子集分配给该索引签名类型。在您的例子中,从exampleType到Record<string,string>的所有内容都是可分配的。 这只能针对对象字面量类型进行检查,因为一旦声明了对象字面量类型,就无法更改它们。因此,索引签名是已知的。

相反,在你使用interface去声明变量时,它们在那一刻类型并不是最终的类型。由于interfac可以进行声明合并,所以总有可能将新成员添加到同一个interface定义的类型上。

8.字符串字面量类型

字符串字面量类型允许你指定字符串必须的固定值。 在实际应用中,字符串字面量类型可以与联合类型,类型保护和类型别名很好的配合。 通过结合使用这些特性,你可以实现类似枚举类型的字符串

type Easing = "ease-in" | "ease-out" | "ease-in-out"; 
class UIElement {
    animate(dx: number, dy: number, easing: Easing) { 
        if (easing === "ease-in") {
        // ... 
        } else if (easing === "ease-out") { 
        } else if (easing === "ease-in-out") {
        } else { 
        // error! should not pass null or undefined. 
        } 
    } 
} 
let button = new UIElement(); 
button.animate(0, 0, "ease-in"); 
button.animate(0, 0, "uneasy"); // error: "uneasy" is not allowed here

9.索引类型

10.映射类型

本网站是一个以CSS、JavaScript、Vue、HTML为核心的前端开发技术网站。我们致力于为广大前端开发者提供专业、全面、实用的前端开发知识和技术支持。 在本网站中,您可以学习到最新的前端开发技术,了解前端开发的最新趋势和最佳实践。我们提供丰富的教程和案例,让您可以快速掌握前端开发的核心技术和流程。 本网站还提供一系列实用的工具和插件,帮助您更加高效地进行前端开发工作。我们提供的工具和插件都经过精心设计和优化,可以帮助您节省时间和精力,提升开发效率。 除此之外,本网站还拥有一个活跃的社区,您可以在社区中与其他前端开发者交流技术、分享经验、解决问题。我们相信,社区的力量可以帮助您更好地成长和进步。 在本网站中,您可以找到您需要的一切前端开发资源,让您成为一名更加优秀的前端开发者。欢迎您加入我们的大家庭,一起探索前端开发的无限可能!