TypeScript是JavaScript类型的超集,它可TypeScript以编译成纯JavaScript
不会被浏览器直接运行,需要编译为JavaScript后才可以
什么是超集尼?
那么我们可以把es6比作成es5的超集,因为es6兼容了es5所有的语法的情况下,还提出了自己的一个新有的独特的一些语法。所以es6包含了es5,我们就可以将es6比作为es5
那么TypeScript 不但包含了JavaScript的es6和es5的特性,还有自己独特的语法特性
TypeScript 的特性
类型系统
从 TypeScript 的名字就可以看出来,「类型」是其最核心的特性。
我们知道,JavaScript 是一门非常灵活的编程语言:
-
它没有类型约束,一个变量可能初始化时是字符串,过一会儿又被赋值为数字。
-
由于隐式类型转换的存在,有的变量的类型很难在运行前就确定。
-
基于原型的面向对象编程,使得原型上的属性或方法可以在运行时被修改。
-
函数是 JavaScript 中的一等公民,可以赋值给变量,也可以当作参数或返回值。
这种灵活性就像一把双刃剑,一方面使得 JavaScript 蓬勃发展,无所不能,从 2013 年开始就一直蝉联最普遍使用的编程语言排行榜冠军;另一方面也使得它的代码质量参次不起,维护成本高,运行时错误多。
而 TypeScript 的类型系统,在很大程度上弥补了 JavaScript 的缺点。
在js中我们声明一个变量后 赋值为一个数字
但是随后我们又赋值了一边
这样说没有问题的
那么也就是动态类型,就是定义的变量类型是动态的
而在ts 当中 我们的类型 都是静态类型
但我们定义一个变量并赋值与它,那么赋值的那个类型 就固定了,那么在未来我们想要修改它的值是时候,如果类型不同,那么就是会报错.
那么这个静态类型 我们写全就是这样的
定义了一个变量b 它的类型必须是一个number 类型
那么ts 不能直接在浏览器下或者node 下运行
这个时候我们非要在浏览器呢是iu时候
我们就需要用的一个包了
cnpm install typescript -g // 安装到全局
我们将原先的dome.js 文件删除,在去执行命令
tsc 文件名
就会出现编译后的dome.js 文件
我们在才可以通过node 去执行的js文件
那么我就必须要要先执行:
tsc 文件名 // 将ts文件编译为 js文件
在执行 node 文件名.js // 在通过node 去执行js文件
那么我们可以直接下载一个包到全局上
cnpm install ts-node -g // 可以执行ts文件
那我们在Ts中定义一个方法
对应的js 就会变成这样
而且我们使用Ts 后会发现
编译器的友好提示
那么我们也可以定义一个外部声明 Point 在将类型 赋给data
总结: Ts的好处
有更好的错误提示
在编译器上有语法提升
类型的声明 可以直观的看到我们写的代码内的一些语意
静态类型的深度理解
现在看来会什么想法?
count 无非就是具备了一个number类型
对于静态类型的理解就是此时的count 永远就是number类型的数字
那么我们还可以深层的去理解
count的类型为number,那么也就具备number这个类型的属性和方法
你可以理解的point变量 一定是一个Point的静态类型
深层理解
point 这个变量上具备Point 类型所以的属性和方法
那么Point上有那个属性,所以我们编译器会友好的语法提示
但我们声明的是静态类型那么不仅我们要知道这个变量的类型是不能修改的 ,那么其他我们也知道它变量内的一些属性和方法基本上也是不变的
基本类型和对象类型
基本类型: string,number,boolean,null,undefined,symbol,void
对象类型
类型注解和类型推断
// type annotation 类型注解 (我们来告诉Ts变量是什么类型)
// type inference 类型推断 (Ts 会自动的尝试分析变量的类型)
let counts: number;
counts = 123
// 在定义的时候,我们通过number 告诉Ts 当前counts 为number类型
// 这样我们就叫它 类型注解
let countsInference = 123;
// 定义一个变量 直接赋值,当我们鼠标放在变量上的时候,自动推断出当前变量的类型是什么
如果Ts 能够自动分析变量的类型,那我们就什么也就不需要做了
如果Ts 无法分析变量类型的话,我们就需要使用类型注解
let fristNumber = 1; // 自动推断
let lastNumber = 2; // 自动推断
const total = fristNumber + lastNumber; // 自动推断
// 无法自动推断
function getTotal (fristNumber, lastNumber) {
return fristNumber + lastNumber
}
const total = getTotal(1, 2)
// 我们无法确定你调用方法的时候会传入什么,所以无法自动推断出类型
那么我们就可以自己添加注解
function getTotal (fristNumber: number, lastNumber: number) {
return fristNumber + lastNumber
}
const total = getTotal(1, 2)
我们既然使用了Ts ,Ts 就是要让我们给每一个变量(属性)都添加上自己的类型
函数相关的类型
// 定义函数
// function hello() {}
// const hello1 = function() {}
// const hello2 = () => {}
// 参数我们会给加入类型注解,但是其实函数有是要有类型注解的
function add (frist: number, last: number): number {
return frist + last
}
// 如果我们不写函数的类型注解
function add (frist: number, last: number) {
return frist + last + ‘’ // 不小心加了一个字符串
}
// 我们发现 你的total就会变成 string类型
const total = add(1, 2)
// void 的意思就空,在函数上添加上就是告诉Ts 这个函数没有返回值
function sayHello (): void {
console.log('hello')
}
// never 的意思就这个函数永远都不可能执行完成
function errorEmtter (): never {
throw new Error();
}
function errorEmtter (): never {
// throw new Error();
while(true) {}
}
// js 解构对象,怎么赋值类型
function setAdd ({ frist, last }) {
return frist + last;
}
const total = setAdd({ frist: 1, last: 2 })
// 错误
function setAdd ({ frist: string, last: string }) {
return frist + last;
}
const total = setAdd({ frist: 1, last: 2 })
// 正确
function setAdd ({ frist, last }: { frist: number, last: number }): number {
return frist + last;
}
const total = setAdd({ frist: 1, last: 2 })
基本语法
基础类型: boolean, number, string, void, undfined, symbol, null
let sum;
sum = 123;
直接赋值的话, 我们发现ts会自动的推断出 类型
而 先声明变量sum , 在对sum赋值,这个时候我们发现sum只能推断出 any;
必须手动推断
let sum: number;
sum = 123;
对象类型:class, object, function, 数组
// 封住一个函数接收一个 string 字符串, 那么我们需要返回一个number类型
// 写法:1
const func = (str: string): number => {
return parseInt(str, 10);
}
// 函数内的参数是必须要推断, 但是返回值是自动推断
// 写法:2
// 也可以这么写
const funcs: (str: string) => number = (str) => {
return parseInt(str, 10);
}
// 函数内的参数是必须要推断, 但是返回值也是必须自动推断的
const data: Date 自动推断出 类型
const data = new Date();
// 其他的 case
// interface 概念:可以用来约束一个函数,对象,以及类的结构和类型
interface Person {
name: 'string'
}
const rawData = '{"name": "dell"}’;
const newData: Person = JSON.parse(rawData);
// temp 这个变量可能是number 也可能是 string
let temp: number | string = 123;
temp = '132’;
数组和元组
数组
我们定义一个数组格式的变量
const numberArr = [1, 2, 3];
自动推断 const numberArr: number[]
你也可以自己注解
const arr: number[] = [1, 2, 3];
numberArr是一个数组,只能是number 类型的
那么万一数组内[1, ‘2’, 3]
那么数组内只能是 数字 或者是 string 类型
const arr: (number | string)[] = [1, '2', 3];
const stringArr: string[] = ['a', 'b', 'c'];
const undfinedArr: undefined[] = [undefined, undefined, undefined];
存储对象内容
const objectArr: {name: string, age: number}[] = [{
name: 'dell’,
age: 123
}]
但是很麻烦, 那么就出现了一个东西 叫做 type alias(类型别名)
type User = {name: string, age: number};
class Teacher {
name: string;
age: number
}
const objectArr: Teacher[] = [
new Teacher(),
{
name: 'dell’,
age: 123,
}
]
元组 tuple
const TeacherInfo: (string | number)[] 约束
const TeacherInfo = ['dell', 'male', 18];
但是 如果 值是 [1, 'male', 18] 这样也是通过的,那么就约束不住了
const TeacherInfo = [1, 'male', 18];
这样就是 元组
const TeacherInfo: [number, string, number] = [1, 'male', 18];
但是如果是 元组类型的话 [number, string, number] 必须一一对应匹配
const TeacherInfo: [number, string, number] = [1, 'male', 18, 1]; // no
// 那么如果是多个列表的话我们该怎么样
const teacherList: [string, string, number][] = [
['dell', 'male', 18],
['sum', 'male', 20],
['jupa', 'male', 23],
]
Interface 接口
// 我们定义二个函数 一个是获取参数,一个是设置参数
const getPersonName = (person) => {
console.log(person)
}
const setPersomName = (person, name) => {
person.name = name
}
// 那么我们会发现现在这样定义是有问题的,因为我们在调用setPerson的时候传递的参数是一个undefined
// 那么我们就可以给方法定义一个类型注解
const getPersonName = (person: {name: string}) => {
console.log(person)
}
const setPersomName = (person: {name: string}, name: string) => {
person.name = name
}
// 那么我们可以看到 我们定义的注解都是长的一样的,写了两次,那么其实是没有必要的
// 定义一个接口 interface:可以用来约束一个函数,对象,以及类的结构和类型
interface Person {
// readonly name: string; // readonly 在属性名前面加上 readonly 就代表此属性是只读的
name: string;
age: number;
sex?: string; // 在属性名后面添加? 就是当前此属性可有可无
[propName: string]: any; // 可以添加任意属性,属性的类型是任意类型 any
}
const getPersonName = (person: Person) => {
console.log(person)
}
const setPersomName = (person: Person, name: string) => {
person.name = name
}
const person = {
name: 'zxd',
age: 123,
sex: '123'
}
getPersonName(person)
setPersomName(person, 'llalla')
类的定义与继承
// 定义一个类
class Person {
name = 'dell';
getName() {
return this.name;
}
}
// 类的继承
class Teacher extends Person {
getTeacherName() {
return 'teacher'
}
// 修改夫类中的方法
getName() {
// 调用父类的方法
return super.getName() + 'haahaha'
// return '我是修改了';
}
}
const teacher = new Teacher();
console.log(teacher.getName())
console.log(teacher.getTeacherName())
类中的访问类型和构造器
// private , protected , public 访问类型
class Person {
// 里面的成员如果直接写的话其实属性名前面默认有一个 public
// public 允许我在类的内外被调用
public name: string;
public sayHi() {
console.log('hi')
}
}
const person = new Person()
person.name = 'zxd'
person.sayHi();
console.log(person.name)
class Person {
// 里面的成员如果直接写的话其实属性名前面默认有一个 public
// public 允许我在类的内外被调用
// private 允许在类内被调用
private name: string;
public sayHi() {
console.log('hi')
}
}
const person = new Person()
person.name = ‘zxd' // no 属性“name”为私有属性,只能在类“Person”中访问。
person.sayHi();
console.log(person.name) // no 属性“name”为私有属性,只能在类“Person”中访问。
class Person {
// 里面的成员如果直接写的话其实属性名前面默认有一个 public
// public 允许我在类的内外被调用
// private 允许在类内被调用
// protected 允许在类内及继承的子类中使用
private name: string;
public sayHi() {
console.log('hi')
}
}
class Teacher extends Person {
public sayBye () {
this.name; // 可以使用父类的属性
}
}
const person = new Person()
person.name = ‘zxd' // no 属性“name”为私有属性,只能在类“Person”中访问。
person.sayHi();
console.log(person.name) // no 属性“name”为私有属性,只能在类“Person”中访问。
// constructor
class Person {
public name: string;
// 构造函数
// 先在类中定义一个name 属性,然后在构造器里面赋值
constructor(name: string) {
this.name = name;
}
}
const person = new Person('dell')
console.log(person.name)
class Person {
// 构造函数
// 先在类中定义一个name 属性,然后在构造器里面赋值
// 如果在构造器的参数内,接受的参数全面 加一个public 其实就等价与上面写的那样
constructor(public name: string) {}
}
const person = new Person('dell')
console.log(person.name)
class Person {
constructor(public name: string) {}
}
class Teacher extends Person {
// 我们只要是继承了其他类后发现 调用constructor 就会报错
// 子类定义构造器,那么子类就需要去调用一下父类的构造器
constructor(public age: number) { // no 派生类的构造函数必须包含 "super" 调用。
}
}
const teacher = new Teacher(28)
静态属性,setter和getter
class Person {
// 我们在类里定义了一个私有的属性,只能在内部去使用
// 那么我们有的时候也需要使用他那么怎么办
constructor(private _name: string) {}
// get 获取私有属性
get name () {
return this._name
}
}
const person = new Person('dell');
console.log(person.name) // 不是一个方法,而是一个属性
class Person {
// 我们在类里定义了一个私有的属性,只能在内部去使用
// 那么我们有的时候也需要使用他那么怎么办
constructor(private _name: string) {}
// get 获取私有属性
get name () {
return this._name
}
// set 修改私有属性
set name (name: string) {
this._name = name
}
}
const person = new Person('dell');
console.log(person.name) // 不是一个方法,而是一个属性
person.name = ‘zxd'
class Person {
// 我们在类里定义了一个私有的属性,只能在内部去使用
// 那么我们有的时候也需要使用他那么怎么办
constructor(private _name: string) {}
// get 获取私有属性
get name () {
return this._name + ' less’ // 加密保护
}
// set 修改私有属性
set name (name: string) {
const realName = name.split(' ')[0]
this._name = name
}
}
const person = new Person('dell');
console.log(person.name) // 不是一个方法,而是一个属性
person.name = ‘zxd less'
抽象类
// 抽象类
// abstract
abstract class Home {
name: string
abstract getName(): number
}
class Person extends Home {
getName() {
return 2123
}
}
将抽象类:就是将共用的方法抽象出来放在抽象类中, 抽象类不能直接new 只能继承子类