TS--(2)接⼝、数组的类型、函数的类型
2019-10-28:
学习内容:接⼝、数组的类型、函数的类型
(展开涉及多个内容)
参考:
⼀、接⼝(
(Interfaces):
⼀、接⼝
(1)
  LabelledValue接⼝就好⽐⼀个名字,⽤来描述上⾯例⼦⾥的要求。它代表了有⼀个label属性且类型为string的对象。需要注意的是,我们在这⾥并不能像在其它语⾔⾥⼀样,说
传给printLabel的对象实现了这个接⼝。我们只会去关注值的外形。只要传⼊的对象满⾜上⾯提到的必要条件,那么它就是被允许的。
  还有⼀点值得提的是,类型检查器不会去检查属性的顺序,只要相应的属性存在并且类型也是对的就可以。
interface LabelledValue {
label: string;
color?: string
}
function printLabel(labelledObj: LabelledValue) {
console.log(labelledObj.label);
}
let myObj = {size: 10, label: "Size 10 Object"};
printLabel(myObj);
// 可选属性:接⼝⾥的属性不全都是必需的。有些是只在某些条件下存在,或者根本不存在。在可选属性名字定义的后⾯加⼀个?符号。可选属性的好处之⼀是可以对可能存在的属性进⾏预定义,好处之⼆是可以捕获引⽤了不存在的属性时的错误(2)只读属性:readonly
  ⼀些对象属性只能在对象刚刚创建的时候修改其值。你可以在属性名前⽤readonly来指定只读属性
interface Point {
readonly x: number;
readonly y: number;
}
let p1: Point = { x: 10, y: 20 };
p1.x = 5;// error!
  只读数组类型:
let a: number[] = [1, 2, 3, 4];
let ro: ReadonlyArray<number> = a;
ro[0] = 12; // error!
ro.push(5); // error!
ro.length = 100; // error!
a = ro; // error!
// 最后⼀⾏可以看到就算把整个ReadonlyArray赋值到⼀个普通数组也是不可以的。解决的⽅法就是类型断⾔重写(就是没有办法)
区别readonly 和 const:const针对变量,readonly针对属性。
(3)额外的属性检查:多数情况下它都是个bug,不建议绕开检查
interface SquareConfig {
color?: string;
width?: number;
}
function createSquare(config: SquareConfig): { color: string; area: number } {
// ...
typeof array}
let mySquare = createSquare({ colour: "red", width: 100 });
//error: 'colour' not expected in type 'SquareConfig'
  注意传⼊createSquare的参数拼写为colour⽽不是color。在JavaScript⾥,这会默默地失败。对象字⾯量会被特殊对待⽽且会经过额外属性检查,当将它们赋值给变量或作为参
数传递的时候。如果⼀个对象字⾯量存在任何“⽬标类型”不包含的属性时,你会得到⼀个错误。
-- 如何绕开检查?
解决办法⼀:
  最佳的⽅式是能够添加⼀个字符串索引签名,前提是你能够确定这个对象可能具有某些做为特殊⽤途使⽤的额外属性。如果SquareConfig带有上⾯定义的类型的color和width属
性,并且还会带有任意数量的其它属性,那么我们可以这样定义它:
interface SquareConfig {
color?: string;
width?: number;
[propName: string]: any;
}
解决办法⼆:
  将这个对象赋值给⼀个另⼀个变量:因为squareOptions不会经过额外属性检查,所以编译器不会报错。
let squareOptions = { colour: "red", width: 100 };
let mySquare = createSquare(squareOptions);
(4)⽤接⼝描述函数类型:(输⼊输出判断)
  为了使⽤接⼝表⽰函数类型,我们需要给接⼝定义⼀个调⽤签名。它就像是⼀个只有参数列表和返回值类型的函数定义。参数列表⾥的每个参数都需要名字和类型。对于函
数类型的类型检查来说,函数的参数名不需要与接⼝⾥定义的名字相匹配。
interface SearchFunc {
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
mySearch = function(src: string, sub: string): boolean {
let result = src.search(sub);
return result > -1;
}
// 函数的参数会逐个进⾏检查,要求对应位置上的参数类型是兼容的。如果你不想指定类型,TypeScript的类型系统会推断出参数类型,因为函数直接赋值给了 SearchFunc类型变量。函数的返回值类型是通过其返回值推断出来的(此例是 false和t
(5)可索引的类型:
  可索引类型具有⼀个索引签名,它描述了对象索引的类型,还有相应的索引返回值类型。
interface StringArray {
[index: number]: string;
}  // 索引签名
let myArray: StringArray;
myArray = ["Bob", "Fred"];
let myStr: string = myArray[0];
// 我们定义了StringArray接⼝,它具有索引签名。这个索引签名表⽰了当⽤ number去索引StringArray时会得到string类型的返回值。
  TypeScript⽀持两种索引签名:字符串和数字。可以同时使⽤两种类型的索引,但是数字索引的返回值必须是字符串索引返回值类型的⼦类型。这是因为当使⽤number来索
引时,JavaScript会将它转换成string然后再去索引对象。也就是说⽤100(⼀个number)去索引等同于使⽤"100"(⼀个string)去索引,因此两者需要保持⼀致。
  字符串索引签名能够很好的描述dictionary模式,并且它们也会确保所有属性与其返回值类型相匹配。因为字符串索引声明了obj.property和obj["property"]两种形式都可以
interface NumberDictionary {
[index: string]: number;
length: number;    // 可以,length是number类型。数字索引的返回值必须是字符串索引返回值类型的⼦类型
name: string      // 错误,`name`的类型与索引类型返回值的类型不匹配,应该是number 类型
}
  最后,你可以将索引签名设置为只读,这样就防⽌了给索引赋值:
interface ReadonlyStringArray {
readonly [index: number]: string;
}
let myArray: ReadonlyStringArray = ["Alice", "Bob"];
myArray[2] = "Mallory"; // error!
// 你不能设置myArray[2],因为索引签名是只读的。
(6)类类型,继承接⼝:将在《类与接⼝》部分补充
⼆、数组的类型:
  回顾:类型+⽅括号表⽰法:number[]
    数组泛型: Array<number>
(1)⽤接⼝表⽰数组:
  NumberArray表⽰:只要索引的类型是数字时,那么值的类型必须是数字。
  虽然接⼝也可以⽤来描述数组,但是我们⼀般不会这么做,因为这种⽅式⽐前两种⽅式复杂多了。类数组除外
interface NumberArray {
[index: number]: number;
}
let fibonacci: NumberArray = [1, 1, 2, 3, 5];
(2)类数组:
  类数组不是数组类型!
  下例除了约束当索引的类型是数字时,值的类型必须是数字之外,也约束了它还有length和callee两个属性
function sum() {
let args: {
[index: number]: number;
length: number;
callee: Function;
} = arguments;
}
内置对象有IArguments, NodeList, HTMLCollection是TS定义好的类型,有⾃⼰的接⼝定义:
function sum() {
let args: IArguments = arguments;
}
interface IArguments {
[index: number]: any;
length: number;
callee: Function;
}
// Type 'IArguments' is missing the following properties from type 'number[]': pop, push, concat, join, and 24 more.
(3)any在数组中的应⽤:
  数组中允许出现任意类型:
let list: any[] = ['xcatliu', 25, { website: 'xcatliu' }];
三、函数的类型:(JS对函数式的⽀持更好)
(1)函数的声明有两种:
1、函数声明:
// 函数声明(Function Declaration)
function sum(x, y) {
return x + y;
}
2、函数表达式:
// 函数表达式(Function Expression)
let mySum = function (x, y) {
return x + y;
};
(2)⼀个函数有输⼊和输出,要在 TypeScript 中对其进⾏约束,需要把输⼊和输出都考虑到:
函数声明的约束:
function sum(x: number, y: number): number {
return x + y;
}
  注意:不允许超出或少于数量的参数
函数表达式的约束:
let mySum: (x: number, y: number) => number = function (x: number, y: number): number {
return x + y;
};
// 对等号右侧的匿名函数进⾏了类型定义,⽽等号左边的 mySum,是通过赋值操作进⾏类型推论⽽推断出来的。⼿动给等号左边添加类型应该这样
  注意:不要混淆了 TypeScript 中的=>和 ES6 中的=>。
     在 TypeScript 的类型定义中,=>⽤来表⽰函数的定义,左边是输⼊类型,需要⽤括号括起来,右边是输出类型。
     在 ES6 中,=>叫做箭头函数,应⽤⼗分⼴泛,
(3)⽤接⼝定义函数的形状,见《接⼝》部分第三part
(4)函数中的可选参数,见《接⼝》第⼀part《可选参数》
  需要注意的是,可选参数必须接在必需参数后⾯。换句话说,可选参数后⾯不允许再出现必需参数了
(5)参数默认值:
  在 ES6 中,我们允许给函数的参数添加默认值,TypeScript 会将添加了默认值的参数识别为可选参数,此时就不受「可选参数必须接在必需参数后⾯」的限制了:function buildName(firstName: string = 'Tom', lastName: string) {
return firstName + ' ' + lastName;
}
let tomcat = buildName('Tom', 'Cat');
let cat = buildName(undefined, 'Cat');
(6)剩余参数的获取:
  使⽤...rest的⽅式获取函数中的剩余参数(rest 参数):
function push(array: any[], ...items: any[]) {
items.forEach(function(item) {
array.push(item);
});
}
let a = [];
push(a, 1, 2, 3);
(7)重载:
  重载允许⼀个函数接受不同数量或类型的参数时,作出不同的处理。
  ⽐如,我们需要实现⼀个函数reverse,输⼊数字123的时候,输出反转的数字321,输⼊字符串'hello'的时候,输出反转的字符串'olleh'。
  这时,我们可以使⽤重载定义多个reverse的函数类型:
function reverse(x: number): number;
function reverse(x: string): string;
function reverse(x: number | string): number | string {
if (typeof x === 'number') {
return String().split('').reverse().join(''));
} else if (typeof x === 'string') {
return x.split('').reverse().join('');
}
}
  我们重复定义了多次函数reverse,前⼏次都是函数定义,最后⼀次是函数实现。在编辑器的代码提⽰中,可以正确的看到前两个提⽰。  注意,TypeScript 会优先从最前⾯的函数定义开始匹配,所以多个函数定义如果有包含关系,需要优先把精确的定义写在前⾯。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。