1 基础类型
1.1 Never
never表示的是那些永不存在值的类型。
例如:never类型是那些总会抛出异常或根本就不会有返回值的函数表达式或箭头表达式的返回值类型;变量也可能是never类型,当它们被永不为真的类型保护所约束时。
下列是一些返回never类型的函数。
function error() : never {
throw new Error()
}
function error2() : never {
return error()
}
function fun() : never {
while (true) {
}
}
2 变量声明
2.1 var、let、const
2.1.1 var 缺陷
1,奇怪的作用域规则:
变量声明在函数的外面是全局作用域,声明的变量是全局变量,可以在整个js代码中生效;
声明在函数内部,是局部作用域,声明的变量是局部变量,仅在函数中生效,而且函数的形参可以看作是不经过var声明的局部变量;
特别注意的是:if语句,for语句等大括号不会开启局部作用域。
// for 中不会开启局部作用域,a是全局变量
for(var a = 1; a < 10; a++) {
}
console.log(a); // a=5
2,捕获变量怪异之处
for (var i = 0; i < 10; i++) {
setTimeout(function() { console.log(i); }, 100 * i);
} //打印 10 10... 而非 0 1 2...
我们传给setTimeout的每一个函数表达式实际上都应用啦相同作用域里的同一个i。setTimeout在若干毫秒后执行一个函数,并且是在for循环结束后。for循环结束后,i的值为10。
2.1.2 let、const
let声明一个变量,他使用的是语法作用域或块作用域。块作用域域在包含它们的块或for循环之外是不能访问的,不能在声明它的代码之前读或写。
块级作用域变量的获取:每次进入一个作用域,它创建了一个变量的环境。就算作用域内代码已执行完毕,这个环境与其捕获的变量依然存在。
当let出现在循环体里时拥有完全不同的行为。不仅是在循环里引入了一个新的变量环境,而是针对每次迭代都会创建这样一个作用域。
for (let i = 0; i < 10; i++) {
setTimeout(function() { console.log(i); }, 100 * i);
} // 打印 0 1 2… 符合预期
const 拥有与let相同的作用域规则,但是不能对它们重新赋值。
2.2 解构
2.2.1 解构数组
let input = [1,"hello"]
let [var1,var2] = input
console.log(var1,var2) //1 hello
作用于函数参数:
let input2 : [number,string] = [1,"hello"]
function fun1([var1,var2] : [number,string]) {
console.log("fun1",var1,var2)
}
fun1(input2) // fun1 1 hello
可以在数组里使用…语法创建剩余变量
let array = [1,2,3,4,5,6,7]
let [first, ...rest] = array
console.log(rest) // [2, 3, 4, 5, 6, 7]
可以忽略不关心的尾元素:
let [number] = array
console.log(number) // 1
或可忽略其他元素:
let [,second,,fourth] = array
console.log(second,fourth) // 2 4
2.2.2 对象解构
let {a,b} = {a:'hello',b:12.0}
console.log(a,b) // hello 12
可以在对象里使用…语法创建剩余变量:
let obj = {nickname: "黄先生",age: 25,sex: "男"}
let {nickname,...otherVar } = obj
console.log(otherVar) // {age: 25, sex: '男'}
默认值:默认值可以让你在属性为undefined时使用缺省值
function fun2({a,b = '默认值'}) {
console.log(b)
}
fun2({a:1}) // 默认值
2.3 展开
展开操作符正与解构相反。
2.3.1 展开数组
let arr1 = [1,2,3];
let arr2 = ['a','b','c']
console.log("[...arr1,...arr2]:",[...arr1,...arr2]) // [...arr1,...arr2]: [1, 2, 3, 'a', 'b', 'c']
console.log("[arr1,arr2]:",[arr1,arr2]) // [arr1,arr2]: [[1,2,3], ['a','b','c']]
2.3.2 展开对象
对象的展开是从左到右进行处理,这意味着出现在展开对象后面的属性会覆盖前面的属性。
let loginInfo = { username: "hmf", password: "123" }
let userInfo = { name: '黄先生', age: 25 }
console.log({...loginInfo,...userInfo, name: '覆盖掉之前的name'}) // {username: 'hmf', password: '123', name: '覆盖掉之前的name', age: 25}
3 接口
3.1 函数类型
需要给接口定义一个调用签名。它就像是一个只有参数列表和返回值类型的函数定义。参数列表里的每个参数都需要名字和类型。
对于函数类型的类型检查来说,函数的参数名不需要与接口里定义的名字相匹配。
interface tableSearchFun {
( key: string, pageSize: number, pageIndex: number ) : boolean;
}
let tableFun1: tableSearchFun = function(key: string, pageSize: number, pageIndex) {
return true;
}
// tableFun1 = function (key: string) {} // 报错
tableFun1 = function (str: String, size: number, index: number) {
return false;
} // 正确,函数类型检查不要求函数的参数名和接口里定义的字段相匹配
3.2 可索引的类型
可索引类型具有一个索引签名,它描述了对象索引的类型,还有相应的索引返回值类型。
TyepeScript支持两种索引签名: 字符串和数字。可以同时使用两种类型的索引,但是数字索引的返回值必须是字符串返回值类型的子类型。(因为当使用number来索引时,js会将它转换成string然后再去索引对象。)
interface Arr {
[index: number]: string;
}
let arr: Arr = ["a","b","c"];
console.log(arr[2]); // c
arr = {1:"k",99:"f"};
console.log(arr[99]); // f
3.3 类类型
3.3.1 实现接口
和Java里的接口基本作用一样,也可以在接口中描述一个方法,在类里实现它。
interface QueryInterface {
key: string;
query(key: string);
}
class TableQuery implements QueryInterface {
key: string;
query(key: string) {
console.log("tableQuery");
}
}
类具有两个类型:静态部分的类型和实例的类型。当用构造器签名去定义一个接口并试图定义一个类去实现这个接口时会得到一个错误。因为当一个类定义一个接口时,只对其实例部分进行类型检查。constructor存在于类的静态部分,所以不在检查范围内。
interface T1 {
new(str: string)
}
class C1 implements T1{
constructor(str: string) {}
} // 报错
3.3.2 继承接口
和类一样,接口也可以相互继承。这让我们能够从一个接口里复制成员到另一个接口里,可以更灵活地将接口分割到可重用的模块里。一个接口可以继承多个接口。
interface People {
name: string;
}
interface Subject {
main: string;
}
interface Student extends People,Subject {
age: number;
}
let st1: Student = <Student>{}
st1.name = "黄";
st1.main = "数学"
st1.age = 18
3.3.3 混合类型
一个对象可以同时作为函数和对象使用,并带有额外的属性。
interface Counter {
(start: number): string;
interval: number;
reset(): void;
}
function getCounter(): Counter {
let counter = <Counter>function (start: number) {
console.log('counter start: ' + start)
}
counter.interval = 10;
counter.reset = function () {
console.log("reset()");
}
return counter;
}
let counter = getCounter();
counter(10); // counter start: 10
counter.interval = 5;
counter.reset(); // reset()
3.3.4 接口继承类
当接口继承一个类类型时,它会继承类的成员但不包括其实现。