安装
npm install -g typescript
检查版本号
tsc -v
实现保存自动编译
文件夹下输入 tsc --init
初始化,生成 tsconfig.json
outDir
配置项为修改编译 js 文件输入的目录
点击终端 -> 运行任务
就可以保存自动编译。
Tip:如果你是第一次配置,可能会和我一样,出现报错,禁止加载脚本,可以参考 这篇文章👈
1. 类型 (Number, String, Boolean, Array, Tuple, Enum, Any, Void,Never)
Boolean
let flag:boolean = true
number
let a:number = 123
a = 12.6 // 浮点类型是可以的,统称数字类型
String
let str:string = 'this is ts'
Array
// 1.(数组里面只能存放数字)
let arr:number[] = [1, 2, 4, 'ashga']
// (数组里面只能存放字符)
let arr:string[] = ['php', 'js', 'c', 2323]
// 如果出现了别的类型,TS 是会报错的
// 这样的写法也是可以的
let arr:Array<number> = [1, 2, 4]
let arr:Array<string> = ['jj', 'b', 'c']
// any 任意类型,这就和之前写法差不多了
let arr3:any[] = ['a', 1, 'o']
Tuple
// 元祖类型(tuple)可以指定数组元素的多种类型
let arr:[string, number, boolean] = ['ts', 3, true]
Enum
语法
enum 枚举名 {
标识符[=整型常数],
标识符[=整型常数],
标识符[=整型常数],
标识符[=整型常数]
}
enum Flag {
success = 1,
error = 0
}
let f:Flag = Flag.error
console.log(f)
enum Color {
red,
blue,
orange
}
let c:Color = Color.orange
console.log(c)
// 未赋值输出的是索引值(和数组索引一致,都是由0开始起算的)
如果为其中的某一个赋值,那么位于该值下方的数据索引依次 + 1
let r:Color = Color.red
let b:Color = Color.blue
let o:Color = Color.orange
console.log(r, b, o) // 0 5 6
Any
typescript 不推荐使用 any 类型,因为那样就失去了使用 typescript 的优势
let num:any = 123
num = 'str'
console.log(num)
null 和 undefined
null 和 undefined 其他(never 类型)数据类型的子类型
let num:number
console.log(num) // 报错 undefined
let num:undefined
console.log(num) // 正确 undefined
let num:number | undefined
num = 123
console.log(num)
// 定义没有赋值, 就是 undefined
let num:number | undefined
console.log(num)
let num:null
num = null
console.log(num)
// 一个元素可能是 number 类型 可能是 null, 可能是 undefined
let num:number | undefined | null
num = 1234
console.log(num)
Void
void 类型 ts中的void表示没有任何类型,一般用于定义方法的时候方法没有返回值
// 方法没有返回任何类型
function run():void{
console.log('run')
}
// 指定方法类型
function run():number{
return 1234
}
run()
Never
never 类型:是其他类型(包括 null 和 undefined)的子类型,代表从不会出现的值
// 返回never的函数必须存在无法达到的终点
function error(message: string): never {
throw new Error(message);
}
2. 函数的定义
函数声明法
// 代表返回值是一个 string 类型
function run():string {
return 'run'
}
// 匿名函数
let fun2 = function():number {
return 123
}
console.log(fun2())
ts 中定义方法传参
function getInfo(name:string, age:number):string {
return `${name}---${age}`
}
console.log(getInfo('sdhgvs', 12))
let getInfo = function(name:string, age:number):string {
return `${name}---${age}`
}
函数无返回值
function getInfo():void{
console.log('hahahah')
}
可选参数
// 注意:可选参数必须配置到参数的最后面
function getInfo(name: string, age?: number): string {
if (age) {
return `${name}---${age}`;
} else {
return `${name}---age is null`;
}
}
console.log(getInfo("zs", 56));
console.log(getInfo('coco'));
默认参数
function getInfo(name: string, age: number = 68): string {
if (age) {
return `${name}---${age}`;
} else {
return `${name}---age is null`;
}
}
console.log(getInfo("zs", 56));
console.log(getInfo('coco'));
剩余参数
function sum(...result: number[]): number {
let sum = 0;
for (let i = 0; i < result.length; i++) {
sum += result[i];
}
return sum;
}
console.log(sum(1, 2, 3, 4, 6, 7, 8, 9, 10));
function sum(a: number, b: number, ...result: number[]): number {
let sum = 0;
for (let i = 0; i < result.length; i++) {
sum += result[i];
}
console.log(a + b);
return sum;
}
函数重载
java 中方法的重载,重载指的是两个或者两个以上同名函数,但是他们的参数不一样,这时会出现函数重载的情况。typescript 中的重载,通过为同一个函数提供多个函数类型定义来试下多种功能的目的。
function getInfo(name: string): string;
function getInfo(age: number): number;
function getInfo(str: any): any {
if (typeof str === "string") {
return "我叫" + str;
} else {
return "我的年龄" + str;
}
}
console.log(getInfo("zs"));
console.log(getInfo(20));
function getInfo(name: string): string;
function getInfo(name: string, age: number): string;
function getInfo(name: any, age?: any): any {
if (age) {
return `我叫${name},我的年龄是${age}`;
} else {
return `我叫${name}`;
}
}
console.log(getInfo("zs"));
console.log(getInfo("zs", 25));
3. 类
类的定义
class Person {
name:string; // 属性
// 构造函数,实例化类的时候触发的方法
constructor(name:string) {
this.name = name;
}
run():void {
console.log(this.name + ' is running')
}
}
let zs = new Person('zs')
zs.run() // zs is running
console.log(zs) // Person { name: 'zs' }
class Person {
name:string;
constructor(name:string) {
this.name = name;
}
getName():string {
return this.name
}
setName(name: string):void {
this.name = name
}
}
let zs = new Person('zs')
console.log(zs.getName())
zs.setName('lisi')
console.log(zs.getName())
继承
class Person {
name: string;
constructor(name: string) {
this.name = name;
}
run(): string {
return `${this.name}在运动`;
}
}
class Web extends Person {
constructor(name: string) {
super(name);
}
run(): string {
return `${this.name}在运动-------`;
}
work(): void {
console.log(`${this.name}在工作`);
}
}
const w = new Web("哈哈哈");
console.log(w.run());
w.work();
类的修饰符
- public:在类里面,子类,类外面都可以访问
- protected:在类里面,子类里面可以访问,类外面没法访问
- private:在类里面可以访问,子类,类外面无法访问
class Person {
public name: string;
protected age?: number;
private sex?: number;
constructor(name: string, age?: number, sex?: number) {
this.name = name;
this.age = age;
this.sex = sex;
}
getPri(): string {
return `性别:${this.sex}`;
}
run(): string {
return `${this.name}在运动`;
}
}
const p = new Person("hehe");
console.log(p.name); // hehe
console.log(p.sex) // 报错:属性“sex”为私有属性,只能在类“Person”中访问
class Web extends Person {
constructor(name: string, age?: number, sex?: number) {
super(name, age, sex);
}
work(): void {
console.log(`${this.name}在工作`);
}
run(): string {
return `${this.age}在运动------- `;
}
// 报错:属性“sex”为私有属性,只能在类“Person”中访问。
getPri(): string {
return `性别:${this.sex}`;
}
}
const w = new Web("哈哈哈", 35);
console.log(w.run());
w.work();
// 属性“age”受保护,只能在类“Person”及其子类中访问。
console.log(w.age)
静态属性和静态方法
class Person {
static str: string = "CoCo";
name: string;
constructor(name: string) {
this.name = name;
}
// 静态方法没法直接调用类里面的属性,只能调用静态属性
static print(): void {
console.log(`static function name ${this.str}`);
// 类型“typeof Person”上不存在属性“name”。
// console.log(`static function name ${this.name}`);
}
}
const p = new Person('lily');
Person.print();
多态
父类定义一个方法不去实现,让继承它的子类去实现,每一个子类有不同的表现。多态属于继承。
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
eat(): void {
console.log(`吃的方法`);
}
}
class Dog extends Animal {
constructor(name: string) {
super(name);
}
eat(): string {
return this.name + "吃骨头";
}
}
class Cat extends Animal {
constructor(name: string) {
super(name);
}
eat(): string {
return this.name + "吃老鼠";
}
}
抽象类
- Typescript 中的抽象类,它是提供其他类继承的基类,不能直接被实例化
- 用 abstract 关键字定义抽象类和抽象方法
- 抽象类中的抽象方法不包含具体实现,并且必须在派生类中实现
- abstract 抽象方法只能放在抽象类里面
abstract class Aminal {
public name: string;
constructor(name: string) {
this.name = name;
}
abstract eat(): any;
}
抽象类和抽象方法用来定义标椎类,这个类要求它的子类必须包含父类中定义的方法
class Dog extends Aminal {
constructor(name: string) {
super(name);
}
eat(): any {
console.log(`${this.name} 在吃饭`);
}
}
let dog = new Dog('仔仔');
dog.eat();
4. 接口
作用:在面向对象的编程中,接口是一种规范的定义,它定义了行为和动作的规范,在程序设计里面,接口起到了一种限制和规范的作用,接口定义了某一批所需遵守的规范,接口不关心这些类的内部状态数据,也不关心这些类里方法的实现细节,它只规定这批内里必须提供某些方法,提供这些方法的类就可以满足实际需要,typescript中的接口类似于java,同时还增加了更灵活的接口类型,包括属性,函数,可索引和类等
就像现实生活中,你要使用这个接口,就必须遵循它的规范
对自定义方法传入参数进行约束
function printLabel(labelInfo: { label: string }): void {
console.log(labelInfo);
}
// 错误
// printLabel('hahah')
// printLabel({name: 'zs'})
// 正确
// 传入的参数必须是 label 开头的
printLabel({ label: "typescript" });
对批量方法传入参数进行约束,interface 定义接口
interface FullName {
firstName: string;
secondName: string;
}
function printName(name: FullName) {
// 必须传入对象 firstName secondName
console.log(name.firstName + ":" + name.secondName);
}
// 错误
// printName('章', '鱼哥')
printName({
firstName: "章",
secondName: "鱼哥",
});
严格按照接口的规范进行传参
interface FullName {
firstName: string;
secondName: string;
}
function printInfo(info: FullName) {
// 类型“FullName”上不存在属性“age”
console.log(info.firstName + info.secondName + info.age)
}
let info = { age: 14, firstName: 'll', secondName: 'mimimi' }
printInfo(info)
可选属性
interface FullName {
firstName: string;
secondName?: string;
}
function getName(name: FullName) {
console.log(name);
}
getName({
firstName: "仔仔",
// secondName: 'hahah',
});
小案例:使用 typescript 对 ajax 进行封装
interface Config {
type: string;
url: string;
data?: string;
dataType: string;
}
function ajax(config: Config) {
let xhr = new XMLHttpRequest();
xhr.open(config.type, config.url, true);
xhr.send(config.data);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
if (config.dataType === "json") {
console.log(JSON.parse(xhr.responseText));
}
}
};
}
ajax({
type: "get",
data: 'name',
url: "http://a.itying.com/api/productlist",
dataType: "json",
});
函数类型接口
对方法传入的参数以及返回值进行约束
interface encrypt {
(key: string, value: string): string;
}
// 函数在定义的时候,必须遵循 encrypt 接口的规范
let md5:encrypt = function(key: string, value: string): string {
return key + value
}
console.log(md5('md5', 'hash'))
可索引接口,数组
interface UserArr {
[index: number]: string;
}
let arr: UserArr = ["a", "b"];
console.log(arr[0]);
let arr1: UserArr = [123, "bbb"]; // 错误
console.log(arr1[0]);
可索引接口,对象的约束
//
interface UserObj {
[index: string]: string;
}
// 数组的索引是 number 类型
let arr: UserObj = ['1'];
let ary: UserObj = { name: "20" };
类类型接口:对类的约束和抽象类有点相似
interface Animal {
name: string;
eat(str: string): void;
}
class Cat implements Animal {
name: string;
constructor(name: string) {
this.name = name;
}
eat(): void {
console.log(`${this.name} is good`);
}
}
var d = new Cat("小黑");
d.eat();
class Fish implements Animal {
name: string;
constructor(name: string) {
this.name = name;
}
eat(food: string): void {
console.log(`${this.name} 在 ${food}`);
}
}
var f = new Fish("旺财");
f.eat("吃骨头");
接口可以继承接口
// 接口扩展,接口可以继承接口
interface Animal {
eat(): void;
}
interface Person extends Animal {
work(): void;
}
class progrommer {
public name: string;
constructor(name: string) {
this.name = name;
}
coding(code: string): void {
console.log(`${this.name} ${code}`);
}
}
// Web 类必须实现 eat 和 work 这两个方法
// Person 接口继承自 Animal 接口
class Web extends progrommer implements Person {
constructor(name: string) {
super(name);
}
eat(): void {
console.log(`${this.name} 吃馒头`);
}
work(): void {
console.log(`${this.name} police`);
}
}
const w = new Web("kola");
w.work();
w.eat();
w.coding("ts code");
5. 泛型
软件工程中,我们不仅要创建一致的定义良好的API,同时也要考虑可重用性。 组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能。在像C#和Java这样的语言中,可以使用泛型来创建可重用的组件,一个组件可以支持多种类型的数据。这样用户就可以以自己的数据类型来使用组件。通俗理解,泛型就是解决
类
,接口
,方法
的复用性以及对不特性数据类型
的支持。
function getData(value: string): string {
return value;
}
我们希望实现同时返回 string 类型 和 number 类型,传入什么,返回什么。支持不特定的数据类型。但不能使用 any,因为 any 会放弃类型检查。
// T 表示泛型,具体什么类型是调用这个方法的时候决定的
function getData<T>(value: T): T {
return value; //传入什么返回什么
}
// 这样调用
getData<number>(123123)
getData<string>("字符串")
类的泛型
获取传入数值的最小值
class MinClass<T> {
public list: T[] = [];
add(value: T): void{
this.list.push(value);
}
min(): T{
let minNum = this.list[0]
for (let i = 0; i < this.list.length; i++) {
if(minNum > this.list[i]) {
minNum = this.list[i]
}
}
return minNum
}
}
let m = new MinClass<number>()
m.add(1)
m.add(2)
m.add(0)
console.log(m.min())
let s = new MinClass<string>()
s.add('t')
s.add('a')
s.add('b')
s.add('c')
console.log(s.min())
泛型接口
interface ConfigFn {
<T>(value: T): T;
}
let getData:ConfigFn = function<T>(value: T): T {
return value
}
getData<string>('张三')
getData<number>(123)
interface ConfigFn<T> {
(value: T): T;
}
function getData<T>(value: T): T {
return value
}
let myGetDataString:ConfigFn<string> = getData
let myGetDataNumber:ConfigFn<number> = getData
myGetDataString('张三')
myGetDataNumber(123)
使用泛型对类的参数进行校验
class ArticleCate {
title: string | undefined;
desc: string | undefined;
status?: number | undefined;
constructor(param: {
title: string | undefined;
desc: string | undefined;
status?: number | undefined;
}) {
this.title = param.title;
this.desc = param.desc;
this.status = param.status
}
}
class MySqlDb<T>{
add(info: T): boolean {
console.log(info)
return true
}
update(info: T, id: number):boolean {
console.log(info)
console.log(id)
return true
}
}
let article1 = new ArticleCate({
title: '那个他',
desc: '言情小说',
status: 1
})
let db = new MySqlDb<ArticleCate>()
db.add(article1)
6. 模块化
你可以这样导出
let dbUrl:string = 'xxxxxx'
export {
dbUrl
}
这样导出
export function getData():any[] {
console.log('获取数据库的数据')
return [
{
title: 'title'
}
]
}
这样导入
import { dbUrl } from './modules/db'
对导入进行重命名
import { getData as get} from './modules/db'
每个模块都可以有一个 default 导出,默认导出使用 default 关键字标记,并且一个模块只能够有一个 default 导出。
function updateData():void {
console.log('更新数据')
}
export default updateData
导入
import updateData from './modules/db'
7. 装饰器
class HttpClient {
constructor() {
}
getData() {
}
}
这是一个类,你希望在不修改当前类的情况下,为该类增加属性和方法
function logClass(params: any) {
console.log(params) // parmas 就是当前的类
params.prototype.apiUrl = '动态扩展的属性'
params.prototype.run = function() {
console.log('function run()')
}
}
这是你希望增加的属性和方法,那我们可以在之前的类前,使用装饰器
@logClass
class HttpClient {
constructor() {
}
getData() {
}
}
测试:实例化
let http = new HttpClient()
console.log(http.apiUrl)
http.run()
对于传参问题
function logClass(params: string) {
// params => 传入的参数
// target => 当前的类
return function(target: any) {
console.log(params, target)
target.prototype.apiUrl = params
}
}
使用该装饰器的时候,就可以进行传参
@logClass('http://www.hello.com')
class HttpClient {
// ....
}
测试
let http = new HttpClient()
console.log(http.apiUrl)