TypeScript 基础知识
快速入门
创建项目文件夹 与 ts 文件夹,在ts文件夹里面添加 ts为后缀的文件 test1.ts 在里面输入一个js 输入代码
输入下列指令安装 typescript
npm i -g typescript
在ts文件夹下进行 tsc xx.ts
如果报错使用下面指令,逐条输入
PS E:\typscriptproject> get-ExecutionPolicy
PS E:\typscriptproject> set-ExecutionPolicy RemoteSigned
PS E:\typscriptproject> get-ExecutionPolicy
变量类型的声明
在test1 里面输入 代码 声明变量的类型这样就可以将js 代码里面的变量类型锁定什么的变量
let a : number;
如果前面直接变量加赋值之后就可以进行自动声明类型
let d =123;
可以在ts 里面写函数
var a=11;
var b=22;
var sum=function (a:number,b:number): number{
return a+b;
}
console.log(sum(a,b));
typescript 中类型的声明
布尔值类型 在typescript 中 可以声明 布尔值类型 boolean
let isDone: boolean=false;
数值类型 number
let decLiteral: number = 6;
let hexLiteral: number = 0xf00d;
// ES6 中的二进制表示法
let binaryLiteral: number = 0b1010;
// ES6 中的八进制表示法
let octalLiteral: number = 0o744;
let notANumber: number = NaN;
let infinityNumber: number = Infinity;
使用空值 void表示返回的函数值
function alertName(): void {
alert('My name is Tom');
}
any 类型就是任意类型(尽量不用) ,如果实在不自动什么类型使用 unknown 类型
null 与 undefined
与 void 的区别是,undefined 和 null 是所有类型的子类型。也就是说 undefined 类型的变量,可以赋值给 number 类型的变量:
let u: undefined = undefined;
let n: null = null;
联合类型
就是变量可以拥有多个类型
let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;
形参也允许拥有多个类型
function getString(something: string | number): string {
return something.toString();
}
数组类型
命名规则 可以用类型 加[]
let fibonacci: number[] = [1, 1, 2, 3, 5];
数组泛型
let f:Array<number>=[1,2,3,4];
这里定义了数组类型的数组
用接口定义数组的结构
interface NumberArray {
[index: number]: number;
}
let fibonacci: NumberArray = [1, 1, 2, 3, 5];
// 上面的泛型规定只有下标是数字,里面的元素也是方向
function sum() {
let args: {
[index: number]: number;
length: number;
callee: Function;
} = arguments;
}
函数声明
传统的函数声明一般是有声明式和表达式两种
// 函数声明(Function Declaration)
function sum(x, y) {
return x + y;
}
// 函数表达式(Function Expression)
let mySum = function (x, y) {
return x + y;
};
在ts 里面可以定义为下面两种
function sum(x: number, y: number): number {
return x + y;
}
sum(1, 2, 3);
let mySum = function (x: number, y: number): number {
return x + y;
};
使用接口形式定义函数
interface SearchFunc {
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
return source.search(subString) !== -1;
}
定义可选参数就是在形参名后面加一个?可选参数放后面
let mysearch :SearchFunc;
mysearch=function(source:string,substring?:string){
if(substring){
if(source==substring){
return true;
}
else{
return false;
}
}
else{
return false;
}
}
console.log(mysearch('s'));
剩余函数参数
function arrPush2(arry,...items){
items.forEach(item => arry.push(item))
return arry;
}
let arr2:number[]=[1,2,4];
console.log(arrPush2(arr2,...[3,3,4]));
重载
上面两个是定义类型
function reverse(x: number): number;
function reverse(x: string): string;
function reverse(x: number | string): number | string | void {
if (typeof x === 'number') {
return Number(x.toString().split('').reverse().join(''));
} else if (typeof x === 'string') {
return x.split('').reverse().join('');
}
}
类型断言
人为规定一个值的类型
一般用上面的那种比较合适
值 as 类型
<类型>值
使用父子断言的形式可以实现多态的效果,在定义形参的时候定义父接口,实参调用的时候使用
interface ApiError extends Error {
code: number;
}
interface HttpError extends Error {
statusCode: number;
}
function isApiError(error: Error) {
if (typeof (error as ApiError).code === 'number') {
return true;
}
return false;
}
- 联合类型(形参有多个类型的情况)可以被断言为其中一个类型
- 父类可以被断言为子类
- 任何类型都可以被断言为 any
- any 可以被断言为任何类型
interface Animal {
name: string;
}
interface Cat {
name: string;
run(): void;
}
let tom: Cat = {
name: 'Tom',
run: () => { console.log('run') }
};
let animal: Animal = tom;
左侧定义的类型大于右侧的类(多态)
双重断言
由于any类型可以被任何类型断言,也可以用于任何类型的断言
interface Cat {
run(): void;
}
interface Fish {
swim(): void;
}
function testCat(cat: Cat) {
return (cat as any as Fish);
}
断言只是在编译阶段修改变量的类型,不是类型转化
断言有点像多态在函数输出类型不可知的情况下直接返回 any 类型
再用的时候进行断言
因此不同赋值的情况下应使用 左边类型大于右边的
interface Animal {
name: string;
}
interface Cat {
name: string;
run(): void;
}
const animal: Animal = {
name: 'tom'
};
let tom = animal as Cat; //左边类型是any
使用泛型来实现灵活的类型
声明语法
declare var 声明全局变量
declare function 声明全局方法
declare class 声明全局类
declare enum 声明全局枚举类型
declare namespace 声明(含有子属性的)全局对象
interface 和 type 声明全局类型
export 导出变量
export namespace 导出(含有子属性的)对象
export default ES6 默认导出
export = commonjs 导出模块
export as namespace UMD 库声明全局变量
declare global 扩展全局变量
declare module 扩展模块
/// <reference /> 三斜线指令
类型别名type
可以将一个混合类型重新命名
type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;
function getName(n: NameOrResolver): Name {
if (typeof n === 'string') {
return n;
} else {
return n();
}
}
type EventNames = 'click' | 'scroll' | 'mousemove'; //字面量类型,可以在多个字符串里面选一个
元组的声明
数组是同类型的组合,元组是不同的类型的组合
元组直接在里面声明了多种类型:
let tom: [string, number] = ['Tom', 25];
可以像数组一样进行操作
枚举类型是对一些常用变量进行匹配
enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat};
enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat};
console.log(Days["Sun"] === 0); // true
console.log(Days["Mon"] === 1); // true
console.log(Days["Tue"] === 2); // true
console.log(Days["Sat"] === 6); // true
console.log(Days[0] === "Sun"); // true
console.log(Days[1] === "Mon"); // true
console.log(Days[2] === "Tue"); // true
console.log(Days[6] === "Sat"); // true
每个值会自动对应一个从零开始的下标也可自定义参数的值
enum Days {Sun = 7, Mon = 1, Tue, Wed, Thu, Fri, Sat};
console.log(Days["Sun"] === 7); // true
console.log(Days["Mon"] === 1); // true
console.log(Days["Tue"] === 2); // true
console.log(Days["Sat"] === 6); // true
未被定义值的变量会承接上一个进行赋值
存在赋值覆盖的问题
enum Days {Sun = 3, Mon = 1, Tue, Wed, Thu, Fri, Sat};
console.log(Days["Sun"] === 3); // true
console.log(Days["Wed"] === 3); // true
console.log(Days[3] === "Sun"); // false
console.log(Days[3] === "Wed"); // true
上面的例子中,递增到 3 的时候与前面的 Sun 的取值重复了,但是 TypeScript 并没有报错,导致 Days[3] 的值先是 "Sun",而后又被 "Wed" 覆盖了。编译的结果是:
带计算式 的赋值要放在最后面(如果还有没赋值的情况)
类
es6 回顾
类和java很想不过属性的的定义要赋值属性
class Animal {
public name;
constructor(name) {
this.name = name;
}
sayHi() {
return `My name is ${this.name}`;
}
}
let a = new Animal('Jack');
console.log(a.sayHi()); // My name is Jack
继承
class Cat extends Animal
get set 方法
class Animal {
constructor(name) {
this.name = name;
}
get name() {
return 'Jack';
}
set name(value) {
console.log('setter: ' + value);
}
}
let a = new Animal('Kitty'); // setter: Kitty
a.name = 'Tom'; // setter: Tom
console.log(a.name); // Jack
static 不需实例化之间调用类名调用
变量的权限
public private 和 protected readonly
TypeScript 可以使用三种访问修饰符(Access Modifiers),分别是 public、private 和 protected。
public修饰的属性或方法是公有的,可以在任何地方被访问到,默认所有的属性和方法都是public的private修饰的属性或方法是私有的,不能在声明它的类的外部访问protected修饰的属性或方法是受保护的,它和private类似,区别是它在子类中也是允许被访问的
class Animal {
public name;
public constructor(name) {
this.name = name;
}
}
let a = new Animal('Jack');
console.log(a.name); // Jack
a.name = 'Tom';
console.log(a.name); // Tom
readonly 使用范围
只读属性关键字,只允许出现在属性声明或索引签名或构造函数中。
只读属性关键字 readonly和其他权限一起使用时,放后面
抽象类
抽象类时用于定义类的外形里面没有实际功能,子类继承抽象类型,对里面的方法进行重写 使用 abstract 进行命名
接口
接口预定义好结构的抽象类,其他类只需要调用就可以了
interface Alart{
types:string;
bark():void;
}
class Car implements Alart{
types: string;
constructor(types){
this.types=types;
}
bark(){
console.log("警告"+`有人${this.types}`);
}
}
let bens= new Car('hh');
bens.bark();
接口继承
接口继承接口
interface A1 {
name:string;
}
interface A2 extends A1{
id:string;
}
class Cars implements A2{
name:'bens';
id='001';
}
let benss= new Cars();
console.log(benss.id);
常见的面向对象语言中,接口是不能继承类的,但是在 TypeScript 中却是可以的
接口继承类
本质上接口继承接口与接口继承类是一样的
class Point {
x: number;
y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
}
interface Point3d extends Point {
z: number;
}
let point3d: Point3d = {x: 1, y: 2, z: 3};
泛型
泛型就是在定义的时候不对类型进行定义,在使用的时候再定义
大致效果与下面类似
function createArray(length: number, value: any): Array<any> {
let result = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
createArray(3, 'x'); // ['x', 'x', 'x']
function createArray<T>(length:number,value:T):Array<T> {
let result: T[]=[];
for(let i=0;i<length;i++){
result[i]=value;
}
return result;
}
console.log(createArray<string>(3,'x'));
多个类型用逗号隔开
function swap<T, U>(tuple: [T, U]): [U, T] {
return [tuple[1], tuple[0]];
}
swap([7, 'seven']); // ['seven', 7]
由于泛型可以自由定义,可定义接口来约束泛型
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
interface CreateArrayFunc {
<T>(length: number, value: T): Array<T>;
}
let createArray: CreateArrayFunc;
createArray = function<T>(length: number, value: T): Array<T> {
let result: T[] = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
createArray(3, 'x'); // ['x', 'x', 'x']
如上就是泛型接口
与泛型接口类似,泛型也可以用于类的类型定义中:
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };
同时泛型是可以可以进行预定义的类型的
function createArray<T = string>(length: number, value: T): Array<T> {
let result: T[] = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
接口合并(相当于类的重载)
interface Alarm {
price: number;
}
interface Alarm {
price: number; // 虽然重复了,但是类型都是 `number`,所以不会报错
weight: number;
}
interface Alarm {
price: number;
alert(s: string): string;
}
interface Alarm {
weight: number;
alert(s: string, n: number): string;
}
只需要接口名字一样就可以,就会进行重载合并