JavaScript的变量类型相当于是动态类型,可以跟随着赋值的改变而类型改变,函数的参数也没有设定类型,所以在定位错误以及安全性上不太够。
说明
1.TS不能被JS解析器直接执行,需要编译成JS执行
2.即使TS编译出错也可以编译成JS
TypeScript的好处
TS的设计目的是解决JS,弱类型和没有命名空间导致很难模块化,不适合开发大型程序的短处(重构更方便)。
TypeScript 是类型安全的JavaScript,JavaScript是弱类型, 很多错误只有在运行时才会被发现,而TypeScript提供了一套静态检测机制, 可以帮助我们在编译时就发现错误
1.支持强类型
2.类型注解:通过类型注解来增加编译时静态类型检查
3.增加了特性:泛型、接口、抽象类等
区别 | TypeScript | JavaScript |
---|---|---|
类型 | 静态 声明时确定类型,不允许修改 |
动态 运行时知道类型,且可以随时改变 |
是否自动转换类型 | 否(大多数时候) | 是 |
何时检查类型 | 编译时 | 运行时 |
基本数据类型 | 增加tuple元组/enum枚举类型/any任意/void/never | boolean/number/string/undefined/null/BigInt、Sybom |
安装TS解析器 :解析器使用nodejs写的 —> 先安装nodej我node已经安装好啦,直接安装TypeScript
使用npm全局安装TypeScript:npm i -g typescript
检查是否安装成功
C:\Users\NINGMEI>tsc --version
Version 4.6.3
创建一个ts文件:后缀以ts结尾
使用tsc对ts文件进行编译,进入到该文件的命令行,使用tsc 文件名
进行编译
D:\ranan\TS>tsc test.ts
# 执行完之后没有输出在这里插入图片描述
类型注解:我们告诉ts是什么类型
语法:变量名:类型
function sum (a:number,b:number):number{
return a + b;
}
一般让TypeScript推导类型,在参数或者返回值上可以使用显式
声明变量如果不指定类型,也不赋值,则TS解析器会自动判断变量的类型为any
从右到左推断
没有明确指出类型的地方,类型推断会帮助提供类型,这种推断发生在初始化变量和成员,设置默认参数值和决定函数返回值时
let a = 1; //推断a的类型为number
最佳通用类型推断
let b = [1, null]; // (1)tsconfig.json中设置"strictNullChecks": true时,推断为[number | null]; (2)'strictNullChecks'设为false时,推断为[number],null是所有类型的子类型
如果没有找到最佳通用类型的话,类型推断的结果为联合类型推断
let arr = [1, 'a'];//let arr: (number | string) = [1, 'a'];
arr = ['a', 'b', 'c', 1, 3, 5, false];
从左到右推断–上下文类型推断(通常出现在事件处理中)
window.onkeydown = (event) => { // event推断为事件类型
console.log(event.button) // 会推断出event有什么属性,button不是键盘事件的属性
}
let str: string = "jimmy";
let num: number = 24;
let bool: boolean = false;
let u: undefined = undefined;
let n: null = null;
let obj: object = {x: 1};
let big: bigint = 100n;
let sym: symbol = Symbol("me");
null和undefined
默认情况下null和undefined是所有类型的子类型,可以给所有的类型赋值
可以在编译文件tsconfig.json
中指定"strictNullChecks":true
,null 和 undefined 只能赋值给 void 和它们各自的类型。
数据类型 | 特点 |
---|---|
any | ①关闭类型检测 ②可以赋值给任意变量 |
unknown | ①类型安全的any,会进行类型检测 ②只能赋值给unknown和any |
void | 常用于函数没有返回值 只能为它赋予undefined和null |
never | 表示永不存在的值 ①函数抛出异常,函数不会执行完毕并返回 ②函数中执行无限循环的代码,使得程序永远无法运行到函数返回值那一步,永不存在返回。 |
function fn1():void {
return;
return null;
return undefined;
//没有return也可以
}
function fn1():never {//这个函数一定报错(抛出错误),函数不能执行完且没有返回值
throw new Error("报错了!");
}
function loopForever(): never { // OK
while (true) {};
}
方法1: 数据类型[]
方法2: 数组泛型,Array<元素类型>
//array,表示某类型的数组,数组中的元素全是该类型
let a:string[] = [1,2,3]//写法1
let a:Array<number> = [1,2,3]//写法2
元组类型表示一个已知元素数量和类型的数组(固定长度和类型的数组),各元素的类型不必相同。
let x: [string, number];
x = ['hello', 10];
可以访问越界元素,使用联合类型替代
x[3] = 'world';
console.log(x[5].toString());
enum Color {//没设置值,初始值默认从0开始按顺序自增,可以理解为数组下标
RED,
PINK,
BLUE,
}
const pink: Color = Color.PINK;
console.log(pink); // 1
enum Color {// 设置初始值,其余也依次递增
RED = 10,
PINK,
BLUE,
}
const pink: Color = Color.PINK;
console.log(pink); // 11
// 字符串枚举 每个都需要声明
enum Color {
RED = "红色",
PINK = "粉色",
BLUE = "蓝色",
}
const pink: Color = Color.PINK; //注意是Color枚举类
console.log(pink); // 粉色
枚举类型提供的一个便利是你可以由枚举的值得到它的名字。知道映射关系的一边就可以知道另外一边
enum Color {Red = 1, Green, Blue}
let colorName: string = Color[2];//注意是string类型
alert(colorName); // 显示'Green'因为上面代码里它的值是2
函数的定义
/*
function add(x, y) {
return x + y;
}
*/
function add(x: number, y: number): number {
return x + y;
}
/*
let z = 100;
function addToZ(x, y) {
return x + y + z;
}
*/
let myAdd = function(x: number, y: number): number { return x + y; };
/*
箭头函数 类型声明 = 函数体
*/
let myAdd: (baseValue: number, increment: number) => number =
function(x: number, y: number): number { return x + y; };
如果函数没有返回任何值,你也必须指定返回值类型为void而不能留空。
语法:{属性名:属性值,属性名:属性值....}
属性个数名字必须一模一样,不能多不能少
let a:{name:string,age:number};
a={name:"xxx"} //不行
a={name:"xxx",age:12}
如果某一属性不确定,可以使用?标识该属性可选
语法:{属性名:属性值,属性名?:属性值
}
如果只确定某属性名,其他属性都可选
语法:{属性名:属性值,[propName:string]:unknown}
propName(可以修改的)表示为string的任意属性名对应的值可以是任意类型
let a:{name:string,[xx:string]:unknown}
a={name:"xxx"}
a={name:"xxx",age:12}
a={name:"xxx",sex:"女"}
类型断言:比TypeScript更了解某个值的详细信息,告诉编译器该变量的类型。
语法:变量 as 类型
或泛型写法
let a:string;
let b:unknown;
//告诉解析器b的实际类型是string
a=b as string;//写法1
a=<string> b;//写法2
// 尖括号 语法
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
// as 语法
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
当strictNullChecks置为true时,类中的成员必须明确定义类型;!
表示在此处告诉编译器,此成员不会为null,不会为undefined;
class Test1{
height!:number //height不会为null或者undefined
}
语法:type 别名=类型
//写法重复
let k: 1|2|3|4|5;
let l: 1|2|3|4|5;
//类型别名
type myType = 1|2|3|4|5;
let k:myType;
let l:myType;
function sum(x: number, y?: number): number {
return x + (y??0);
}
sum(3);//3
也可以理解为?:
指的是参数自动加上undefined
function getval(x: number, y?: number)
function getval(x: number, y: number||undefined)
?.用来判断左侧的表达式是否是 null | undefined
,如果是则会停止表达式运行,可以减少我们大量的&&运算。
可以看成&&
比如我们写出a?.b
时,
理解1:a ? a.b :undefined
理解2:如果a为null或者undefined,则后面的表达式等于没有,如果存在则获取a.b
a === null || a === void 0 ? void 0 : a.b;
当为链式时需要同时不为null或者undefined
function getData(data: any){
let name = data?.row?.name //data && data.row ? data.row.name:undefined
}
//普通写法
function getData(data: any){
let name;
if (data && data.row) {
name = data.row.name
}
}
undefined这个值在非严格模式下会被重新赋值,使用void 0必定返回真正的 undefined
??类似||,它们之间的区别
|| 表达式,对 false、‘’、NaN、0 等逻辑空值也会生效
??在左侧表达式结果为 null 或者 undefined 时,才会返回右侧表达式
比如我们书写了let b = a ?? 10
,a为null或者undefined时取b,生成的代码如下:
let b = (a !== null && a !== void 0) ? a : 10;
通过 &
运算符可以将现有的多种类型叠加到一起成为一种类型
type PersonName = { name: string }
type Person = PersonName & { age: number }
| 或 满足其一
let a: 10|20; //a=10或者a=20
let b:boolean | string; //b可以是boolean或者是string类型
& 且都需要满足
let j:{name:string} & {age:number}
j={name:'ranan',age:18)
class类学习笔记
ES6的class可以看作只是一个语法糖,看成构造函数的另一种写法
class Point{}
typeof Point //'function'
Point === Poin.prototype.constructor //true
static静态属性(方法)/类属性(方法):不需要创建实例就可以使用,是属于类的通过"类名.属性(方法)"调用
实例属性(方法):是属于实例化出的这个对象。通过"对象.属性(方法)"调用
readonly属性:表示该属性只读不可以修改,和static连用需要放在其后面
说明
1.类的所有方法都定义在类的prototype属性上
2.实例的属性除非显式定义在本身(this),其余都是定义在原型上
class Person{
name:"ranan" //定义实例属性
readonly age:15 //定义只读属性
call(){} //定义实例方法
}
构造函数在对象创建时(new时)调用
构造器中的使用的属性必须先声明(JS不用)
class Person{
//注意这个需要指定不然会报错说Person上面没有name属性!JS不用
name:string;
age:number;
constructor(name:string,age:number){
this.name = name;
this.age = age;
} //在实例方法中,this表示当前的实例
}
子类继承父类相当于子类的proto指向父类Father的prototype,相当于子类 = new 父类()
1.可以继承父类的方法
2.可以重写父类的方法,但是重写之后没办法获取到父类的同名方法
class Animal{
name:string;
age:number;
constructor(name:string,age:number){
this.name=name;
this.age = age;
}
sayHello(){
console.log("动物在叫");
}
}
class Dog extends Animal{
sayHello(){}//重写父类方法
run(){} //添加新方法
}
class Cat extends Animal{
sayHello(){}//重写父类方法
}
const dog = new Dog('王朝',12);
const cat = new Cat('咪咪',14);
class A{
constructor(name:string){}
sayHello(){}
}
class B extends A{} //没有指定构造器时默认是下面的写法
class B extends A{
constructor(name:string,age:number){
super(name);
this.age = age;
}
}
class C extends A{
sayHello(){
super.sayHello()
}
}
abstract class Animal{
name:string;
age:number;
constructor(name:string,age:number){
this.name=name;
this.age = age;
}
abstract sayHello():void;
abstract reNum():number;
}
public
关键字修饰的属性为共有属性,可以在任意位置访问和修改(默认值)protected
关键字修饰的属性为保护属性,可以在此类和其子类中访问和修改private
关键字修饰的属性为私有属性,只能在此类里访问和修改
ts中设置存取器
get 属性名(){}
表示getter方法,在属性被读取时自动调用
set 属性名(){}
表示setter方法,在属性被修改时自动调用
class Person{
private name:string; //Person类外不可以访问
constructor(name:string){
this.name = name;
}
get name(){
return this.name
}
set name(value){
this.name = value;
}
}
const per = new Person("ranan");
console.log(per.name);//读取属性自动调用get name(){}
per.name = "biubiu"; //修改属性set name(){}
class Person{
public name:string;
constructor(name:string){
this.name = name;
}
}
//简洁写法
class Person{
constructor( public name:string){
this.name = name;
}
}
接口可以看成规则,实现接口就必须满足接口设置的规则
语法 interface 接口名
接口作为类型声明,那么这个类型就需要满足接口的规则
注意点:如果有两个同名接口,会合并成一个接口
interface myInterface{//定义接口的规则
name:string,
age:number,
?sex:string //可选属性
}
const obj:myInterface = {//obj对象必须满足接口的规则有name和age
name:'xxx',
age:111
}
interface exampleFunc {
(name:string,age:number): void
}
接口可以看成规则,对类进行约束
说明
定义类时可以使类去实现一个接口(满足接口定义的规则),一个子类只能有一个父类,但是可以实现多个接口
interface myInterface{ //接口定义必须有name和sayHello
name:string,
sayHello():void;
}
class MyClass implements myInterface{
name:string;
constructor(name:string){
this.name = name;
}
sayHello(){}
}
typy A = {
a:string
}
type B = {
b:string
}&A
const a: B = {
a:'hi'
b:'hi'
}
interface A {
aa:string
}
interface B extends A {
bb:string
}
const aa: B = {
aa:"hi"
bb:"hi"
}
区别 | 抽象类 | 接口 |
---|---|---|
继承 | 抽象类是类,一个类只能继承一个抽象类 | 一个类可以实现多个接口 |
方法 | 可以是普通方法和抽象方法 | 全是抽象方法 |
子类 | 子类必须覆盖抽象类的抽象方法 | 子类必须遵守接口定义的规则,接口中的所有东西都得有 |
抽象类是对类本质的抽象,表达的是 is a 的关系。比如:male is a Human。抽象类包含并实现子类的通用特性
接口是对行为的抽象,表达的是 like a 的关系。比如:Baoma like a plane(它有飞的功能一样可以飞),但其本质上 is a Car。接口的核心是定义行为,即实现类可以做什么
泛型指是在定义函数或者类时并不知道类型,在被使用时才知道数据的类型。
泛型可以指定多个
//<泛型名>定义泛型
function fn <T> (a:T):T{//参数和返回值类型一致
return a;
}
fn(10); //此时T是number,利用类型推断
fn <string>("hello") //显式指定T是string
限定泛型的范围
interface Inter{//定义接口
length:number;
}
function fn3<T extends Inter>(a:T):number{//这里的泛型必须继承Inter接口
return a.length;
}
完成了两件事情
这两件事情完全独立,即使代码有类型错误或者说没有通过类型检查也可以编译成旧版本的JavaScript
其中编译选项 noEmitOnError
可以禁用错误输出
TypeScript编译器包含了一些能够影响语言核心方法的设置。
noTmplicitAny
:控制是否可以有隐式any属性
strictNullChecks
控制null和undefined在每种类型中是否被允许。可以防止“未定义不是对象”等一系列的运行是错误