TypeScript真香系列的内容将参考中文文档,但是文中的例子基本不会和文档中的例子重复,对于一些地方也会深入研究。另外,文中一些例子的结果都是在代码没有错误后编译为JavaScript得到的。如果想实际看看TypeScript编译为JavaScript的代码,可以访问TypeScript的在线编译地址,动手操作,印象更加深刻。
交叉类型是将多个类型合并为一个类型,相当于一种并的操作。
interface IDog {
name: string,
age: number,
}
interface ICat {
name: string,
color: string
}
let animal: IDog & ICat;
animal = {
name: "哈士奇",
age: 1,
color: "white",
}
animal.name; // "哈士奇"
animal.age; // 1
上面animal中的属性一个都不能少,如果少了属性的话,就会出现下面的错误:
i
nterface IDog {
name: string,
age: number,
}
interface ICat {
name: string,
color: string
}
let animal: IDog & ICat;
animal = { //错误,color属性在ICat中是必须的
name: "哈士奇",
age: 1,
// color: "white",
}
联合类型可以说是和交叉类型相反,声明的类型不确定,可以是多个类型中的一个或几个。
let a: number | string;
a = 1;
a = "s";
a = false; // 错误,类型false不能分配给类型 number | string
看一个和交叉类型相对应的例子:
interface IDog {
name: string,
age: number,
}
interface ICat {
name: string,
color: string
}
let animal: IDog | ICat; // 这里我们把&改成了|
animal = { //没有报错
name: "哈士奇",
age: 1,
// color: "white",
}
animal.name;
animal.age;
再看一个例子:
interface IDog {
name: string,
age: number,
}
interface ICat {
name: string,
color: string
}
let animal: IDog | ICat;
animal = {
name: "哈士奇",
age: 1,
color: "white",
}
animal.name;
animal.age; //错误,age不存在于ICat. age不存在于IDog | ICat
我们可以看见上面的例子出现了错误,这是因为TypeScript编译器age不知道是IDog还是ICat,所以只能访问公共的name属性。如果我们想要访问这个属性的话,该怎么办?我们可以使用类型断言:
interface IDog {
name: string,
age: number,
}
interface ICat {
name: string,
color: string
}
let animal: IDog | ICat;
animal = {
name: "哈士奇",
age: 1,
color: "white",
}
animal.name;
(<IDog>animal).age; // 1
这下就能访问age属性了。
有时候我们会遇到类似于下面这种场景:
interface IDog {
name: string,
age: number,
}
interface ICat {
name: string,
color: string
}
function animal(arg: IDog | ICat): any {
if (arg.color) { //错误
return arg.color //错误
}
}
但是上面的代码会出现错误。如果想要上面这段代码正常工作,可以和联合类型中的例子一样,使用类型断言:
interface IDog {
name: string,
age: number,
}
interface ICat {
name: string,
color: string
}
function animal(arg: IDog | ICat):any {
if ((<ICat>arg).color) {
return (<ICat>arg).color;
}
}
除了类型断言,我们还可以利用类型保护来进行判断,常用的类型保护有三种:typeof类型保护,instanceof类型保护和自定义类型保护。
function animal(arg: number | string): any {
if (typeof arg === "string") {
return arg + "猫";
}
}
typeof类型保护只有两种形式能被识别: typeof v === "typename"和 typeof v !== "typename"。"typename"必须是 "number", "string", "boolean"或 "symbol"。
class Dog {
name: string;
age: number;
constructor() {
};
}
class Cat {
name: string;
color: string;
constructor() {
};
}
let animal: Dog | Cat = new Dog();
if (animal instanceof Dog) {
animal.name = "dog";
animal.age = 6;
}
if (animal instanceof Cat) {
animal.name = "cat";
animal.color = "white";
}
console.log(animal); //Dog {name: "dog", age: 6}
instanceof的右侧要求是一个构造函数,TypeScript将细化为:
对于一些复杂的情况,我们可以自定义来进行类型保护:
interface IDog {
name: string,
age: number,
}
interface ICat {
name: string,
color: string
}
let animal: IDog | ICat;
animal = {
name: "哈士奇",
age: 6,
}
function isDog(arg: IDog | ICat): arg is IDog {
return arg !== undefined;
}
if (isDog(animal)) {
console.log(animal.age); //6
}
类型别名可以给类型取一个别名。类型别名和接口类似,但又有不同。
type Name = number;
type Types = number | string;
type NAndT = Name & Types;
type MyFunc = () => number;
function animal(arg: Types) {
return arg;
}
animal("哈士奇"); //"哈士奇"
类型别名可以作用于原始类型、联合类型、泛型等等。
type Dog<T> = { value: T };
function dog(arg: Dog<string>) {
return arg;
}
dog({ value: "哈士奇" }); //{value: "哈士奇"}
dog({ value: 1}); //错误,类型number不能分配给string
dog("哈士奇"); //错误,参数“哈士奇”不能分配给类型 Dog<string>
区别一:接口可以创建新的名字,而且可以在其它任何地方使用;类型别名不创建新的名字,而是起一个别名。
区别二:类型别名可以进行联合,交叉等操作。
区别三:接口可以被extends和implements以及声明合并等,而类型别名不可以。
这里介绍一下声明合并:
“声明合并”是指编译器将针对同一个名字的两个独立声明合并为单一声明。 合并后的声明同时拥有原先两个声明的特性。
任何数量的声明都可被合并;不局限于两个声明。
举个例子:
interface IDog {
name: string;
setName(arg:string): string;
}
interface IDog {
age: number;
}
interface IDog {
color: string;
}
let dog: IDog;
dog = {
color: "black",
age: 6,
name: "哈士奇",
setName: (arg) => {
return arg
}
}
合并之后:
interface IDog {
color: string;
age: number;
name: string;
setName(arg:string): string;
}
我们可以看出,后面的接口在合并后出现在了靠前的位置。
字符串字面量允许我们指定字符串为必须的固定值。
type Dog = "哈士奇" | "泰迪" | "中华田园犬" | "萨摩耶";
function dog(arg: Dog):any {
switch (arg) {
case "哈士奇":
return "傻狗";
case "泰迪":
return "精力旺盛";
case "中华田园犬":
return "忠诚";
case "萨摩耶":
return "微笑天使";
}
}
dog("哈士奇"); //"傻狗"
dog("柯基"); //错误,参数"柯基"不能分配给类型Dog
数字字面量同理。
type Num = 1 | 2 | 3 | 4 | 5 | 6 | 7;
function week(arg: Num):any {
switch (arg) {
case 1:
return "星期一";
case 2:
return "星期二";
case 3:
return "星期三";
case 4:
return "星期四";
case 5:
return "星期五";
case 6:
return "星期六";
case 7:
return "星期日";
}
}
week(6); //"星期六"
week(8); //错误
我们可以合并单例类型、联合类型、类型保护和类型别名来创建一个叫做可辨识联合的高级模式。它具有三个要素:
可以看看下面这个例子就能理解了:
//我们首先声明了将要联合的接口,目前各个接口之间是没有联系的,
//只是都有一个kind的属性(可以称为可辨识特征或标签)但是有不同的字符串字面量类型
interface IColor {
kind: "color";
value: string;
}
interface ISize {
kind: "size";
height: number;
width: number;
}
//然后我们利用类型别名和联合类型把两个接口联合到一起
type MyType = IColor | ISize;
//最后使用可辨识联合
function types(arg: MyType) :any{
switch (arg.kind) {
case "color":
return arg.value;
case "size":
return arg.height * arg.width;
}
}
types({ kind: "color", value: "blue" }); //"blue"
types({ kind: "size", height: 10, width: 20 }); //200
使用索引类型,编译器就能够检查使用动态属性名的代码。我们首先要知道两个操作符。
interface IDog{
a: string,
b: number,
c: boolean,
}
let dog: keyof IDog; //let dog: "a" | "b" | "c"
let arg: IDog["a"]; //let arg: string
再看一个较为复杂的例子:
interface IDog{
name: string,
age: number,
value: string,
}
let dog: IDog;
dog = {
name: "二哈",
age: 6,
value: "奥里给"
}
function myDog<T, K extends keyof T>(x: T, args: K[]): T[K][] {
return args.map(i => x[i]);
}
myDog(dog, ['name']); //["二哈"]
myDog(dog, ['name', 'value']); //["二哈", "奥里给"]
myDog(dog, ['key']); //错误,类型不匹配
有时候我们可以会遇到这种情况,把每个成员都变为可选或者只读:
interface Person{
name: string;
agent: number;
}
interface PersonPartial {
name?: string;
age?: number;
}
interface PersonReadonly {
readonly name: string;
readonly age: number;
}
这在JavaScript里经常出现,TypeScript提供了从旧类型中创建新类型的一种方式 — 映射类型。 在映射类型里,新类型以相同的形式去转换旧类型里每个属性。
interface IPerson{
name: string;
agent: number;
}
type OPartial<T> = {
[P in keyof T]?: T[P];
}
type OReadonly<T> = {
readonly [P in keyof T]: T[P];
}
//使用方式
type PersonPartial = OPartial<IPerson>;
type ReadonlyPerson = OReadonly<IPerson>;
原文:https://github.com/zhongsp/TypeScript/blob/master/handbook/advanced-types.md
在JavaScript中存在这样两种原始类型:Null与Undefined。这两种类型常常会使JavaScript的开发人员产生疑惑,在什么时候是Null,什么时候又是Undefined?Undefined类型只有一个值,即undefined。当声明的变量还未被初始化时,变量的默认值为undefined。
主要介绍了JS中检测数据类型的几种方式,typeof运算符用于判断对象的类型,但是对于一些创建的对象,它们都会返回\'object\',有时我们需要判断该实例是否为某个对象的实例,那么这个时候需要用到instanceof运算符
对于object和number、string、boolean之间的转换关系,ToPrimitive是指转换为js内部的原始值,如果是非原始值则转为原始值,调用valueOf()和toString()来实现。
Undefined类型表示未定义,它的类型只有一个值为undefined。undefined和null有一定的表意差别。非整数的Number类型无法使用 == 或 === 来比较,因为 JS 是弱类型语言,所以类型转换发生非常频繁
近在做项目代码重构,其中有一个要求是为代码添加智能提示和类型检查。智能提示,英文为 IntelliSense,能为开发者提供代码智能补全、悬浮提示、跳转定义等功能,帮助其正确并且快速完成编码。
基本类型:按值访问,可以操作保存在变量中实际的值;引用类型数据存在堆内存,而引用存在栈区,也就是说引用类型同时保存在栈区和堆区,关于==的执行机制,ECMASript有规范,因为==前后的值交换顺序,返回的值也是一样的,所以在此对规范做出如下总结
JavaScript 是一种弱类型或者说动态类型语言。所以你不用提前声明变量的类型,在程序运行时,类型会被自动确定,你也可以使用同一个变量保存不同类型的数据。
js的值传递和引用(地址)传递:js的5种基本数据类型 number,string,null,undefined,boolean 在赋值传递时是值传递,js的引用数据类型(object,array,function)进行引用传递,其实底层都是对象。
JS中所有的值都可以转换成布尔类型 使用Boolean()或者 !!(两个感叹号),JS中所有的值都可以转换成数字类型,使用Number()或+。数字类型转换场景目的只有一个,用于计算,将后台传递的数据,从字符串转换为数字并参与计算
众所周知,JS在很多情况下会进行强制类型转换,其中,最常见两种是:1.使用非严格相等进行比较,对==左边的值进行类型转换2.在if判断时,括号内的值进行类型转换,转化为布尔值
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!