一、TypeScript简介
1、TypeScript 是什么?
2、TypeScript 增加了什么?
二、开发环境搭建
1、ts需要编译为js,然后才能交给我们js解析器去执行,所以这时候我们要先装一个ts解析器。ts解析器是用node.js写的,所以要先安装node.js。
2、使用npm全局安装typescript
- 进入命令行
- 输入:npm i -g typescript
3、创建一个ts文件
4、使用tsc对ts文件进行编译
- 进入命令行
- 进入ts文件所在目录
- 执行命令:tsc xxx.ts
三、TS的类型声明
1、声明一个变量a,同时指定它的类型为number
let a: number;
a = 10;
a = 33;
2、如果变量的声明和赋值是同时进行的,TS可以自动对变量进行类型检测
let c = false;
c = true;
3、声明可以在函数中用,一个是参数类型,一个是返回值类型
function sum(a: number, b: number): number{
return a + b;
}
let result = sum(123,456)
四、TS中类型
1、字面量
let a: 10;
a = 10;
可以使用 | 来连接多个类型(联合类型)
let b: "male" | "female";
b = "male";
b = "female";
let c: boolean | string;
c = true;
c = 'hello';
2、any 表示的是任意类型,一个变量设置类型为any后相当于对该变量关闭了TS的类型检测,使用TS时,不建议使用any类型。
any 表示任意类型
// 声明变量如果不指定类型,则TS解析器会自动判断变量的类型为any (隐式的any)
let d;
d = 10;
d = 'hello';
d = true;
unknown 表示未知类型的值
let e: unknown;
e = 10;
e = 'hello';
e = true;
any和unknown两者之间的区别
2.1 any类型的变量,可以赋值给任意变量
// s的类型是string,d的类型是any
let s: string;
let d;
s = d;
2.2 unknown 实际上就是一个类型安全的any
// s的类型是string,e的类型是unknown
let s: string;
let e: unknown;
代码报错
// unknown类型的变量,不能直接赋值给其他变量
s = e;
正确写法
// 做类型判断
if(typeof e === "string"){
s = e;
}
或者
// 类型断言,可以用来告诉解析器变量的实际类型
/*
* 语法:
* 变量 as 类型
* <类型>变量
**/
s = e as string;
s = <string>e;
3、void
void 用来表示空,以函数为例,就表示没有返回值的函数
// 函数返回 number
function(num) : number{
return 123;
}
// 函数返回 string或number
function(num) : string | number{
return true;
}
// 函数没有返回值
function(num) : void{
// return;
// return undefined;
// return null;
}
4、never
never 表示永远不会返回结果
function(): never{
throw new Error('报错了!');
}
5、object
object 表示一个js对象
let a: object;
a = {};
a = function(){};
定义对象结构
// {}用来指定对象中可以包含哪些属性
// 语法:{属性名:属性值,属性名:属性值}
// 在属性名后面加上?,表示属性是可选的
let b: {name: string , age?:number};
b = {name: '孙悟空', age:18};
或者
b = {name: '孙悟空'}
// [propName: string]: any 表示任意类型的属性
let c: {name: string , [propName: string]: any]};
c = {name: '孙悟空' , age: 18, gender: '男'’};
定义函数结构
/*
* 设置函数结构的类型声明:
* 语法:(形参:类型,形参:类型 ...)=> 返回值
* **/
let d: (a:number ,b:number) => number;
d = function (n1,n2): number{
return n1 + n2;
}
6、array
数组的类型声明:
- 类型【】
- Array<类型>
// string[] 表示字符串数组
let e: string[];
e = ['a','b','c'];
// number[] 表示数值数组
let f: number[];
let g: Array<number>;
g = [1,2,3];
7、tuple
元祖,元祖就是固定长度的数组
let h: [string,number];
h = ['hello', 123];
8、enum
枚举
enum Gender{
Male = 0,
Female =1
}
let i: {name: string, gender: Gender};
i = {
name: '孙悟空',
gender: Gender.Male // 'male'
}
console.log(i.gender === Gender.Male );
&表示同时
let j: {name: string } & { age: number};
j = {name: '孙悟空',age:18};
类型的别名
type myType = 1 | 2 | 3 | 4 | 5;
let k:myType;
let l:myType;
let m:myType;
k = 2;
五、TS编译选项
1、自动编译文件
- 编译文件是,使用-w指令后,TS编译器会自动监视文件的变化,并在文件发生变化时对文件进行重新编译。
tsc xxx.ts -w
2、自动编译整个项目
- 如果直接使用tsc指令,则可以自动将当前项目下的所有ts文件编译为js文件
- 但是能直接使用tsc命令的前提是,要先在项目根目录下创建一个ts的配置文件 tsconfig.json
- tsconfig.json是一个JSON文件,添加配置文件后,只需要tsc命令即可完成对整个项目的编译
3、tsconfig.json 配置选项
- include: 用来指定哪些ts文件需要被编译
- exclude: 不需要被编译的文件目录
- compilerOptions:编译器的选项
http://json.schemastore.org/tsconfig
六、面向对象
1、类(class)
对象中主要包含了两部分: 属性 方法
class Person{
// 实例属性
name: string = '孙悟空';
// 在属性前面使用static关键字可以定义类属性(静态属性)
static age: number = 18;
// 定义方法
sayHello(){
console.log('Hello 大家好');
}
static sayBye(){
console.log('拜拜');
}
}
const per = new Person();
// 实例属性
console.log(per.name);
per.sayHello();
// 静态属性
console.log(Person.age);
Person.sayBye();
2、构造函数和this
this就表示当前对象
class Dog{
name: string;
age: number;
// 构造函数
// 构造函数会在对象创建时调用
constructor(name: string, age: number) {
// 在实例方法中,this就表示当前的实例
// 在构造函数中当前对象就是当前新建的那个对象
// 可以通过this向新建的对象中添加属性
this.name = name;
this.age = age;
}
bark(){
// 在方法中通过this来表示当前调用方法的对象
console.log(this);
}
}
const dog = new Dog('小黑',4);
const dog2 = new Dog('小白',2);
dog2.bark();
3、继承
使用继承后,子类将会拥有父类所有的方法和属性
(function (){
// 定义一个Animal类
class Animal{
name: string;
age: number;
constructor(name: string, age: number){
this.name = name;
this.age = age;
}
sayHello(){
console.log('动物在叫~');
}
}
// 定义一个表示狗的类
// Animal被称为父类,Dog被称为子类
class Dog extends Animal {
run(){
console.log(`${this.name}在跑~~`);
}
// 如果在子类中添加了和父类相同的方法,则子类方法会覆盖掉父类方法,这种子类覆盖掉父类的方法,我们称为重写
sayHello(){
console.log('汪汪汪汪!);
}
}
// 定义一个表示猫的类
class Cat extends Animal {
sayHello(){
console.log('喵喵喵喵!);
}
}
const dog = new Dog('旺财',5);
const cat = new Cat('咪咪',3);
console.log(dog);
dog.sayHello();
dog.run();
console.log(cat );
cat .sayHello();
})();
4、super关键字
如果在子类中写了构造函数,在子类的构造函数中必须对父类的构造函数调用
super();
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
sayHello(){
console.log('动物在叫~');
}
}
class Dog extends Animal{
age: number;
constructor(name: string, age: number) {
// 如果在子类中写了构造函数,在子类的构造函数中必须对父类的构造函数调用
super(name);
this.age = age;
}
sayHello() {
// 在类的方法中 super就表示当前类的父类
// super.sayHello();
console.log('汪汪汪汪!')
}
}
const dog = new Dog('旺财',3);
dog.sayHello();
5、抽象类
-以 abstract 开头的类是抽象类
-抽象类中可以有抽象方法,实质方法,真实的属性
// 抽象类不能用来创建对象
// 抽象类是专门用来被继承的类
// 抽象类中可以添加抽象方法
abstract class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
// 定义一个抽象方法
// 抽象方法使用 abstract 开头,没有方法体
// 抽象方法只能定义在抽象类中,子类必须对抽象方法进行重写
abstract sayHello();
}
class Dog extends Animal{
sayHello() {
console.log('汪汪汪汪!')
}
}
class Cat extends Animal{
sayHello() {
console.log('喵喵喵喵!')
}
}
const dog = new Dog('旺财');
dog.sayHello();
6、接口
-接口定义一个标准,限制某个类必须符合该标准
-接口用来定义一个类结构,用来定义一个类中应该包含哪些属性和方法
-在接口中所有的方法都是抽象方法
//描述一个对象的类型
type myType = {
name: string,
age: number
};
interface myInterface{
name: string;
age: number;
}
interface myInterface{
gender: string;
}
const obj: myType = {
name: 'sss',
age: 111,
gender: '男'
}
// 接口中所有的属性都不能有实际的值
// 接口只定义对象的结构,而不考虑实际值
interface myInter{
name: string;
sayHello():void;
}
// 定义类时,可以使用类去实现一个接口
// 实现接口就是使类满足接口的要求
class MyClass implements myInter{
name: string;
constructor(name: string){
this.name = name;
}
sayHello(){
console.log('大家好~~');
}
}
7、属性的封装
public:修饰的属性可以在任意位置访问(修改)默认值
private:私有属性只能在类内部进行访问(修改)
class Person{
// TS 可以在属性前添加属性的修饰符
private name: string;
private age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
// 定义方法,用来获取name属性
getName(){
return this.name;
}
// 定义方法,用来设置name属性
setName(value: string){
this.name = value;
}
getAge(){
return this.age;
}
setAge(value: number){
// 判断年龄是否合法
if(value >= 0){
this.age= value;
}
}
}
const per = new Person('孙悟空',18);
// 现在属性是在对象中设置的,属性可以任意的被修改
// 属性可以任意被修改将会导致对象中的数据变得非常不安全
per.setName('猪八戒');
per.setAge(-33);
console.log(per);
-属性存储器
getter 用来读取属性
setter 用来设置属性
// TS中设置getter方法的方式
get name(){
return this.name;
}
set name(value){
this.name = value;
}
get age(){
return this.age;
}
set age(value){
if(value >= 0){
this.age = value;
}
}
const per = new Person('孙悟空', 18);
per.name = '猪八戒';
per.age= -33;
console.log(per);
protected:受保护的属性,只能在当前类和当前类的子类中访问(修改)
class A{
protected num: number;
constructor(num: number){
this.num = num;
}
}
class B extends A{
test(){
console.log(this.num);
}
}
const b = new B(123);
b.num = 33;
特殊语法
calss C{
name: string;
age: number
construtor(name: string, age: number){
this.name = name;
this.age = age;
}
}
//等价于
calss C{
// 可以直接将属性定义在构造函数中
constructor(public name: string, public age: number){
}
}
const c = new C('xxx',111);
console.log(c);
七、泛型
定义泛型好处,不用指定具体类型而且也能体现出属性和返回参数类型是相同的。
function fn(a: number) :number{
return a;
}
// 在定义函数或是类时,如果遇到类型不明确就可以使用泛型
function fn<T>(a:T):T{
return a;
}
let result = fn(10);// 不指定泛型,TS可以自动对类型进行推断
let result2 = fn<string>('hello'); //指定泛型
泛型可以同时指定多个
function fn2<T, K>(a: T,b: K):T{
console.log(b);
return a;
}
fn2<number,string>(123,'hello');
有些情况下想要限制泛型范围
接口中有泛型
interface Inter{
length: number;
}
// T extends Inter 表示泛型T必须实现Inter接口
function fn3<T extends Inter>(a: T): number{
return a.length;
}
// 正确
fn3('123');
fn3({length: 10});
// 错误
fn3(123);
fn3({name: 'hello'});
类中有泛型
class MyClass<T>{
name: T;
constructor(name: T) {
this.name = name;
}
}
const mc = new MyClass<string>('yilia');