目录
TypeScript变量声明
变量是计算机中用来存储数据的“容器”,它可以让计算机变得有记忆。
注意:变量不是数据本身,它们仅仅是一个用于存储数值的容器。
TypeScript变量的命名规范:
• 变量名称可以包含数字和字母。
• 变量名称不能以数字开头。
• 除了_和$符号外,不能包含其他特殊字符,包括空格。
• 不能使用TypeScript和Javascript内置的一些英语词汇,比如:number、string、enum、void等。
• 字母严格区分大小写,大小写不一致的变量代表着不同的变量。
变量使用之前必须声明,可以使用let和var来进行声明。我们可以使用以下四种方式来声明变量。
声明变量的类型及初始值。
let 变量名: 类型 = 值;
// 例如
let num: number = 123;
声明变量的初始值,但不设置类型,该变量的类型为初始值的类型。
let 变量名 = 值;
// 例如
let num = 123;
num = 'a';
// Type 'string' is not assignable to type 'number'.
// 类型'string'不能赋值给类型'number'。
声明变量的类型,但不设置初始值,该变量的初始值为undefined。
let 变量名: 类型;
// 例如
let num: number;
console.log(num);
// undefined
声明变量,但不设置类型和初始值,该变量的类型为any类型,初始值为undefined。
let 变量名;
// 例如
let num;
console.log(num);
num = 123;
num = 'a';
// undefined
类型推论(Type Inference)
如果没有明确的指定类型,那么TypeScript会依照类型推论(Type Inference)的规则推断出一个类型。
let num = 2;
num = "12";
// Type 'string' is not assignable to type 'number'.
// 类型'string'不能赋值给类型'number'。
上述例子中虽然没用指定类型,但是在编译的过程中会报错,事实上以上代码等价于:
let num: number = 2;
num = "12";
// Type 'string' is not assignable to type 'number'.
// 类型'string'不能赋值给类型'number'。
TypeScript会在没有明确指定一个类型的时候推断出一个类型,这就是类型推论。
如果定义的时候没有赋值,不管之后哦有没有赋值,都会被推断成any
类型而不被类型检查。
let num;
num = 12;
num = "12";
等价于:
let num: any;
num = 12;
num = "12";
联合类型(Union Types)
联合类型(Union Types)表示的是取值可以为多种类型中的一种,通过(|)将变量设置为多种类型。
注意:只能赋值指定类型,如果赋值其它类型就会报错。
let val: string | number;
val = 12;
val = "Runoob";
val = null;
// Type 'null' is not assignable to type 'string | number'.
// 类型'null'不能分配给类型'string | number'。
上述例子中let val:string|number
的含义是,允许val
的类型是string
或者number
,其他类型就会报错。
联合类型的属性和方法
当TypeScript不确定一个联合类型的变量到底时哪个类型的时候,我们只能访问此联合类型中所有类型的共有的属性和方法。
let val: string | number = 123;
console.log(val.length);
// Property 'length' does not exist on type 'number'.
// 属性'length'在类型'number'上不存在。
let val: string | number = "123";
console.log(val.toString());
// 运行正常
当联合类型当作参数使用时,可以先判断类型,在做进一步的操作。
function log(val: string | string[]) {
if(typeof val === "string") {
console.log(val);
} else {
val.forEach(item => {
console.log(item);
});
}
}
log("a");
log(["b", "c"]);
// "a" "b" "c"
联合类型的数组
我们还可以将数组声明为联合类型。
let arr :number[] | string[];
arr = [1, 2, 3];
arr.forEach(item => {
console.log(item);
});
arr = ["a", "b", "c"];
arr.forEach(item => {
console.log(item);
});
// 1 2 3
// "a" "b" "c"
对象的类型-接口
在 TypeScript中,我们使用接口(Interfaces)来定义对象的类型。
什么是接口
接口时一系列抽象方法的声明,是一些方法特征的集合,这些方法应该都是抽象的,需要具体的类去实现,然后第三方就可以通过这组抽象方法去调用,让具体的类执行具体的方法。
TypeScript中的接口是一个非常灵活的概念,除了可用于对类的一部分行为进行抽象以外,也常用于对对象的形状(Shape)进行描述。
interface ICat {
name: string;
age: number;
};
let tom: ICat = {
name: 'Tom',
age: 25
};
上述例子中,我们定义了一个接口Cat
,接着定义了一个变量tom
,他的类型是Cat
。这样我们就约束了tom
的形状必须和接口Cat
一致。
接口的命名准则:
• 接口名称可以是名词、名词短语或者是描述行为的形容词。
• 使用大小写。
• 谨慎使用小写。
• 在接口名称前面加上字母I
,以表示类型为接口。
• 定义类/接口的时候,请使用类似的名称。名称之间的区别仅在于接口名称前面有一个字母I
。
• 不要使用下划线字符(_)。
定义的变量比接口多一些属性是不允许的。
interface ICat {
name: string;
};
let tom: ICat = {
name: 'Tom',
age: 25
};
// Type '{ name: string; age: number; }' is not assignable to type 'Cat'.
// 类型'{ name: string; age: number; }'不能赋值给类型'Cat'。
// Object literal may only specify known properties, and 'age' does not exist in type 'Cat'.
// 对象字面量只能指定已知属性,并且'age'在类型'Cat'中不存在。
定义的变量比接口少一些属性是不允许的。
interface ICat {
name: string;
age: number;
};
let tom: ICat = {
name: 'Tom'
};
// Property 'age' is missing in type '{ name: string; }' but required in type 'Cat'.
// 属性'age'在类型'{name: string;}',但在类型'Cat'中需要。
所以赋值的时候,变量的形状必须和接口的形状保持一致。
可选属性
有时候在赋值的时候我们不希望变量的形状和接口保持一致,那么可以使用可选属性:
interface ICat {
name: string;
age?: number;
};
let tom: ICat = {
name: 'Tom'
};
interface ICat {
name: string;
age?: number;
};
let tom: ICat = {
name: 'Tom',
age: 25
};
可选属性的含义是该属性可以不存在,但是这时仍然不允许添加未定义的属性或者减少必要的属性。
interface ICat {
name: string;
age?: number;
};
let tom: ICat = {
name: 'Tom',
age: 25,
sex: "男"
};
// Type '{ name: string; age: number; sex: string; }' is not assignable to type 'Cat'.
// 类型'{ name: string; age: number; sex: string; }'不能赋值给类型'Cat'。
// Object literal may only specify known properties, and 'sex' does not exist in type 'Cat'.
// 对象字面量只能指定已知属性,而'sex'在类型'Cat'中不存在。
interface ICat {
name: string;
age?: number;
};
let tom: ICat = {
age: 25
};
// Property 'name' is missing in type '{ age: number; }' but required in type 'Cat'.
// 属性“name”在类型“{ age: number; }',但在类型'Cat'中需要。
任意属性
有时候我们希望一个接口允许有任意的属性,可以使用如下方式:
interface ICat {
name: string;
age?: number;
[propName: string]: string;
};
let tom: ICat = {
name: 'Tom',
gender: 'male',
color: "grey",
};
[propName: string]: string
的意思是,定义了接口中的任意属性为string
类型的值。
需要注意的是,一旦定义了任意属性,那么确定属性和可选属性的类型必须是它的类型的子集。
interface ICat {
name: string;
age?: number;
[propName: string]: string;
};
let tom: ICat = {
name: 'Tom',
gender: 'male',
color: "grey",
age: 15
};
// Property 'age' of type 'number | undefined' is not assignable to 'string' index type 'string'.
// 类型为'number | undefined'的属性'age'不能分配给类型为'string'的'string'。
// Type '{ name: string; age: number; gender: string; color: string; }' is not assignable to type 'Cat'.
// 类型'{ name: string; age: number; gender: string; color: string; }'不能赋值给类型'Cat'。
// Property 'age' is incompatible with index signature.
// 属性'age'与索引类型不兼容。
// Type 'number' is not assignable to type 'string'.
// 类型'number'不能赋值给类型'string'。
上述例子中,任意属性的值允许的类型是string
,但是可选属性age
的值的类型却是number
,number
不是string
的子属性,所以就导致了报错。
一个接口中只能定义一个任意属性,如果接口中有多个类型的属性,则可以再任意属性中使用联合属性。
interface ICat {
name: string;
age: number;
[propName: string]: string | number;
};
let tom: ICat = {
gender: 'male',
color: "grey",
name: 'Tom',
age: 15
};
只读属性
有时候我们希望对象中的一些字段只能再创建的时候被赋值,那么可以用readonly
定义只读属性。
interface ICat {
readonly id: number;
name: string;
age: number;
[propName: string]: string | number;
};
let tom: ICat = {
id: 9527,
gender: 'male',
color: "grey",
name: 'Tom',
age: 15
};
tom.id = 3208;
// Cannot assign to 'id' because it is a read-only property.
// 不能分配给'id',因为它是只读属性。
上述例子中,使用readonly
定义的属性id
初始化之后,又被赋值了,所以报错了。
注意,只读的约束存在于第一个给对象赋值的时候,而不是第一次给自读属性赋值的时候。
interface ICat {
readonly id: number;
name: string;
age: number;
[propName: string]: string | number;
};
let tom: ICat = {
gender: 'male',
color: "grey",
name: 'Tom',
age: 15
};
tom.id = 3208;
// Property 'id' is missing in type '{ gender: string; color: string; name: string; age: number; }' but required in type 'Cat'.
// 在'Cat'类型中需要类型'{ gender: string; color: string; name: string; age: number; }'中的'id'属性。
// Cannot assign to 'id' because it is a read-only property.
// 不能分配给'id',因为它是只读属性。
上述例子中,有两处发生了报错,第一个是在给对象tom
赋值的时候没有对id
进行赋值,发生了报错;第二个是给tom
中的只读属性id
进行了赋值,发生了报错。
接口和数组
接口中我们可以将数组的索引值和元素设置为不同的类型,索引值可以是数字或者是字符串。
interface IColor {
[index: number]: string;
};
let colorArr: IColor = ["red", "pink", "black"];
let colorArr: IColor = ["red", 1, "black"];
// Type 'number' is not assignable to type 'string'.
// 类型'number'不能赋值给类型'string'。
let colorArr: IColor;
colorArr["r"] = "red";
// Element implicitly has an 'any' type because index expression is not of type 'number'.
// 元素隐式具有'any'类型,因为索引表达式不是'number'类型。
上述例子中,IColor
定义一个数组索引值是number
类型,值是string
类型,如果使用了其他类型则会报错。
interface IColor {
[index: string]: number;
};
let colorArr: IColor;
colorArr["r"] = 1;
interface IColor {
[index: string]: string;
};
let colorArr: IColor;
colorArr["r"] = "red";
接口继承
接口继承就是接口可以通过其他接口来扩展自己,TypeScript中使用extends
实现接口继承接口。
单继承时使用extends
继承即可,多继承时,需要继承的每个接口使用,
分割。
interface IName {
name: string;
};
interface ICat extends IName {
age: number;
};
let tom: ICat = {
name: "Tom",
age: 12
};
上述例子中,ICat
继承了IName
中的属性,所以在给对象tom
赋值的时候IName
中的属性和ICat
中的属性都要添加。
interface IName {
name: string;
};
interface IAge {
age: number;
};
interface ICat extends IName, IAge {};
let tom: ICat = {
name: "Tom",
age: 12
};
上述例子中,ICat
继承了IName
和IAge
中的属性,所以在给对象tom
赋值的时候IName
中的属性和IAge
中的属性都要添加。