TypeScript 是 JavaScript 的类型的超集,它能够编译成纯 JavaScript。编译出来的 JavaScript 能够运行在任何浏览器上。TypeScript 编译工具能够运行在任何服务器和任何系统上。TypeScript 是开源的。html
如下内容均出自于 TS入门教程node
以及 Ts 官网的一些内容,没有基础的小伙伴直接看打了⭐️的内容便可。jquery
看了以后怎么搭个环境写一写?git
mkdir demo
cd demo
touch 1.ts
复制代码
打开vscode,打开控制台,切换到问题 tabes6
欧了,开始尝试 ts 吧github
let isDone: boolean = false;
// 使用构造函数 Boolean 创造的对象不是布尔值
复制代码
是全部类型的子类型npm
void类型不能赋值给 number编程
let u: undefined = undefined;
let n: null = null;
let num: number = undefined;
let u: undefined;
let num: number = u;
复制代码
通常表示函数没有返回值。用在变量上没有什么卵用。json
function warnUser(): void {
console.log("This is my warning message");
}
let a: void = undefined
let a: void = 'undefined' // 报错,这是字符串
复制代码
跟它类似的类型还有
undefined
和null
在不开启严格空检查的状况下--strictNullChecks
,他们能够赋值给全部已经定义过***其余类型***的变量。 也就是说他们是全部类型的子类型数组
let a: undefined = undefined
let a: null = null
复制代码
TypeScript里的全部数字都是浮点数。 这些浮点数的类型是 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;
复制代码
单双引
''
""
,模板字符的都被视为字符串
let str:string = ''
复制代码
有多种声明数组的方式
类型 + []
来表示数组:const arr: number[] = [1,2,3]
const arr2: string[] = ['1','2']
复制代码
const arr2: Array<number> = [1,2,3,3]
const arr2: Array<string> = [1,2,3,3]
复制代码
interface NumArr {
[index: number]: number;
}
let numArr: NumArr = [1,2,3];
复制代码
let list:any[] = [1,"z",{}]
复制代码
// 表示一个肯定数组长度和类型的写法
const arr:[string,number] = ['2',3]
复制代码
就是伪数组的定义
官方已给了各自的定义接口 Arguments
, NodeList
, HTMLCollection
function sum() {
let args: IArguments = arguments;
}
复制代码
js中没有这类型,仿照强类型语言来的。值只能为数字,不定义默认值得状况为从0开始。
enum Color {Red, Green, Blue}
let c: Color = Color.Green;
// c = 1
enum Number {one = 10, two}
let c: Number = Number.two;
// c = 11
复制代码
指代全部的类型
let a: any = '123'
let a = 123; // 不声明默认 any
复制代码
表示永远不存在的值,通常会用来写抛出异常或推断为返回值为never的函数。(好比return一个其余的never类型)
function error(message: string): never {
throw new Error(message);
}
error('a')
复制代码
非简单类型 也就是除number,string,boolean,symbol,null或undefined以外的类型。
function create(o: object | null): void{
console.log(o);
};
create({ prop: 0 }); // OK
create(null); // OK
create([]); // OK
create('a'); // error
复制代码
在 TypeScript 中,咱们使用接口(Interfaces)来定义对象的类型。
对对象的描述
接口通常首字母大写。
赋值的时候,变量必须和接口保持一致。
interface Person {
name: string;
age: number;
}
let tom: Person = {
name: 'Tom',
age: 25
};
复制代码
不想彻底匹配某个接口,经过
?
表示这个属性是可选的仍然不容许添加未定义的属性
interface Person {
name: string;
age?: number;
}
let tom: Person = {
name: 'Tom'
};
复制代码
让接口容许添加任意的属性值
[propName: string]: any;
interface Person {
name: string;
age?: number;
[propName: string]: any;
}
let tom: Person = {
name: 'Tom',
gender: 'male'
};
复制代码
一旦定义了任意属性, 那么肯定属性和
?
可选属性都必须是任意属性的子集
interface Person {
name: string;
age?: number;
[propName: string]: string;
}
let p:Person = {
name: 'zzc',
age: 12, // error , 定义了 propName 必须将值设定为 string 类型
gender: 'male' ,
}
复制代码
至关因而常量了,初次赋值后不能从新赋值 作为变量使用的话用
const
,若作为属性则使用readonly
。
interface demo {
readonly a: string; // readonly定之后不能改值
b: number
}
let obj: demo = {
a: 'ss',
b: 1
}
obj.a = 'aa' // error
obj.b = 2 // success
复制代码
只读的约束存在于第一次给对象赋值的时候,而不是第一次给只读属性赋值的时候
interface Person {
readonly id: number;
}
const tom: Person = {} // error
tom.id = 1 // error,
复制代码
会报两次错,第一个是由于指定了 id,但没有给 id 赋值
第二个错是给只读属性id赋值了
经过
ReadonlyArray
定义的数组,再也没法改变了。
let a: number[] = [1, 2, 3, 4];
let ro: ReadonlyArray<number> = [1,2,3];
a[0] = 10 // success
ro[0] = 12; // error!
ro.push(5); // error!
ro.length = 100; // error!
a = ro; // 注意! 将readonly的值赋值给一个可变得数组也是不行的。
a = ro as Array<any> // 可是能够用断言重写
复制代码
常见的函数声明方式有: 函数声明 & 函数表达式
用 ts 定义函数要考虑它的输入和输出
function sum(a:number,b:number):number{
return a+b
}
// 形参和实参数量要一致
sum(1) // error
sum(1,2) //3
sum(1,2,3) // error
复制代码
// 方式 1
let sum = function(a:number,b:number):number {
return a + b;
}
// 方式二
let sum: (x: number, y: number) => number = function (x: number, y: number): number {
return x + y;
};
复制代码
方式一中只对等号右侧的匿名函数定义了类型,左边是ts经过类型推论定义出来的
方式二才是给 sum 定义类型,**其中的 =>
不是 es6的 =>
** ,它用来表示函数的定义,左边是输入类型,须要用括号括起来,右边是输出类型。
由于和 es6箭头函数可能形成混淆,最好用方式一;
经过?
给函数定义可选参数
可选参数后面不容许再出现必须参数了
若是给参数添加了默认值,ts 会自动识别为可选,且不受上一条规则的限制。
function sum(a:number,b?:number){}
function sum(a?:number,b:number){} // error
function sum(a:number = 1,b:number){} // 默认值,识别为可选,且不报错
复制代码
使用…rest获取剩余参数,使用数组类型去定义它
剩余参数必须是函数的最后一个参数
function (a, ...items:any[]){}
function (...items:any[], a){} // error
复制代码
重载容许一个函数接受不一样数量或类型的参数时,做出不一样的处理。
能够重复定义一个函数的类型
function say(somthing:string):string; function say(somthing:number):string; // 以上是函数定义 // 如下是函数实现 function say(somthing:string|number):string|number {
return somthing
}
复制代码
注意,TypeScript 会优先从最前面的函数定义开始匹配,因此多个函数定义若是有包含关系,须要优先把精确的定义写在前面。
类型断言(Type Assertion)能够用来手动指定一个值的类型。
判定这个变量的类型是啥
类型断言不是类型转换
两种写法
<类型>值
or值 as 类型
若是在 tsx 语法中使用,必须用
as
联合类型能够指定一个变量为多种类型,此变量只能访问类型们的共有方法。
但一些状况下咱们必须使用某一类型的方法或属性时,就能够用断言
function say(something:number|string):void{
alert(something.length) // 联合类型,报错
}
// ==> 使用断言, 在变量前加上 <类型>
function say(something:number|string):void{
alert( (<string>something).length ) // success } 复制代码
断言成一个联合类型中不存在的类型是不容许的
function say(something:number|string):void{
alert(<boolean>something.length) // 联合类型没有 boolean ,error } 复制代码
第三方库会暴露出一个变量,让咱们在项目中直接使用。
可是 ts 编译时不知道这是啥,编译没法经过。
此时咱们就要用
declare var
声明语句来定义他的类型
// 好比 jquery
$('div') // ERROR: Cannot find name 'jQuery'.
// ==> 使用 declare var 第三方库变量: (参数: string) => 返回类型
declare var $: (selector: string) => any;
$('#foo'); // success
复制代码
declare var
并非真正的声明一个变量,编译完会删除,仅仅是定义类型。
一般咱们会把声明语句放到一个单独的文件(
*.d.ts
)中,这就是声明文件声明文件必需以
.d.ts
为后缀假如仍然没法解析,那么能够检查下
tsconfig.json
中的files
、include
和exclude
配置,确保其包含了jQuery.d.ts
文件。
// src/jQuery.d.ts
declare var jQuery: (selector: string) => any;
复制代码
这只是非模块化项目中使用的例子
固然,jQuery 的声明文件不须要咱们定义了,社区已经帮咱们定义好了:jQuery in DefinitelyTyped。
咱们能够直接下载下来使用,可是更推荐的是使用 @types
统一管理第三方库的声明文件。
@types
的使用方式很简单,直接用 npm 安装对应的声明模块便可,以 jQuery 举例:
npm i @types/jquery -D
复制代码
能够在这个页面搜索你须要的声明文件。
声明文件有如下方法
- 全局变量:经过
<script>
标签引入第三方库,注入全局变量- npm 包:经过
import foo from 'foo'
导入,符合 ES6 模块规范- UMD 库:既能够经过
<script>
标签引入,又能够经过import
导入- 模块插件:经过
import
导入后,能够改变另外一个模块的结构- 直接扩展全局变量:经过
<script>
标签引入后,改变一个全局变量的结构。好比为String.prototype
新增了一个方法- 经过导入扩展全局变量:经过
import
导入后,能够改变一个全局变量的结构这里只记录 npm 导入的方法, 其余请看 书写声明文件
在给一个第三方库写声明文件以前,先查看这个库有没有声明文件。通常来讲,npm 包的声明文件可能存在于两个地方:
package.json
中有type
字段,或者有 index.d.ts
声明文件。通常经常使用的包都有了,本身要发布 npm 包的时候最好也绑定在一块儿。npm install @types/foo --save-dev
直接经过安装。若是都没有,才本身写。
声明文件存放的位置是有约束的,通常在两个位置。
node_modules
建立第三方库的声明文件,但这种通常不采纳。通常 node_modules
不会随咱们的应用发布到服务器|git上。types
目录来写,要配合tsconfig.json
来使用。# 项目结构
├── README.md
├── src
| └── index.ts
├── types
| └── foo
| └── index.d.ts
└── tsconfig.json
复制代码
{
"compilerOptions": {
"module": "commonjs",
"baseUrl": "./",
"paths": {
"*" : ["types/*"]
}
}
}
复制代码
无论采用了以上两种方式中的哪种,我都强烈建议你们将书写好的声明文件(经过给原做者发 pr,或者直接提交到
@types
里)发布到开源社区中,享受了这么多社区的优秀的资源,就应该在力所能及的时候给出一些回馈。只有全部人都参与进来,才能让 ts 社区更加繁荣。
npm 包写的声明文件 declare
不会声明一个全局变量,只有导出的时候才会应用类型声明。
export const name: string; // 简单类型
export function getName(): string; // 函数 export class Animal { // class 声明
constructor(name: string);
sayHi(): string;
}
export interface Options { // 接口
data: any;
}
// ===> 对应使用到项目中
import { name, getName, Animal, Directions, Options } from 'foo';
let myName = getName();
let cat = new Animal('Tom');
let options: Options = {
data: {
name: 'foo'
}
}
复制代码
declare
export
经过 declare 定义多个变量,一次性导出
declare const name: string;
declare function getName(): string; declare class Animal {
constructor(name: string);
sayHi(): string;
}
export {
name,
getName,
Animal,
}
复制代码
只有
function
、class
和interface
能够直接默认导出,其余的变量须要先定义出来,再默认导出针对默认导出,通常会把导出语句烦恼歌在声明文件的最前面。
export default function foo(): string; export default interface Options {
data: any
}
export default class Person {
constructor(name: string);
sayHi(): string;
}
declare const str:string;
export default str;
复制代码
export namespace
namespace
原本是 TS 的模块化方案,随着 es6愈来愈屌基本已经不在 ts 中使用了。可是声明文件中仍是很经常使用的,表示变量是一个包含了子属性的对象类型。
像是
lodash
,它是个对象,但提供了不少子属性方法如lodash.debunce
若是对象拥有深层的层级,则须要用嵌套的
namespace
来声明深层的属性的类型:总的来讲,用来导出一个拥有子属性的对象。
export namespace obj {
const name: string;
function fn(a:string,b?:nnumber):void; class Event {
say(str:string):void
};
// 若是还有包含子属性的对象,就嵌套
namespace sonObj {
const foo: string;
}
}
复制代码
当一个变量,既是函数又是对象。能够组合多个语句声明。
export function objAndFn(foo:string):any; export namespace objAndFn {
function fn(boo:number):void; const name:string; } 复制代码
commonjs
规范用如下方式来导出:
// 总体导出
module.exports = foo;
// 单个导出
exports.bar = bar;
复制代码
在 ts 中,针对这种导出,有多种方式能够导入,第一种方式是 const ... = require
:
// 总体导入
const foo = require('foo');
// 单个导入
const bar = require('foo').bar;
复制代码
第二种方式是 import ... from
,注意针对总体导出,须要使用 import * as
来导入:
// 总体导入
import * as foo from 'foo';
// 单个导入
import { bar } from 'foo';
复制代码
第三种方式是 import ... require
,这也是 ts 官方推荐的方式:
// 总体导入
import foo = require('foo');
// 单个导入
import bar = require('foo').bar;
复制代码
对于 commonjs
规范的库,须要使用 export = 变量
的语法写声明文件
准确地讲,export =
不只能够用在声明文件中,也能够用在普通的 ts 文件中。实际上,import ... require
和 export =
都是 ts 为了兼容 AMD 规范和 commonjs 规范而创立的新语法
export = foo;
declare function foo():string; declare namespace foo {
const bar: nnumber;
}
复制代码
TS定义了 js 中内置对象的类型,在 TypeScript 核心库的定义文件中。
包括 ECMAscript、DOM、Bom 等
这些内置对象的类型在
.ts
中均可以直接使用
let b: Boolean = new Boolean(1);
let e: Error = new Error('Error occurred');
let d: Date = new Date();
let r: RegExp = /[a-z]/;
let body: HTMLElement = document.body;
let allDiv: NodeList = document.querySelectorAll('div');
document.addEventListener('click', function(e: MouseEvent) {
// Do something
});
复制代码
还会在该内置对象中定位错误
Math.pow(10, '2'); // error 必须是两个 number 型
document.addEventListener('click', function(e) {
console.log(e.targetCurrent);
// error, MouseEvent 类型不存在 targetCurrent属性
});
复制代码
内置对象不包含 Node ,若是要使用
npm install @types/node --save-dev
复制代码
这章是介绍面向对象编程
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
console.log(this.greeting); // world
}
greet() {
return "Hello, " + this.greeting;
}
}
let greeter = new Greeter("world");
复制代码
基本继承 做为基类的类通常被叫作超类,继承的类叫作派生类
class Car {
name: string = '大众';
by() {
console.log(this.name);
}
}
class Baoma extends Car {
name: string = '宝马'
}
let bm = new Baoma()
bm.by()
复制代码
在子类
constructor
构造函数中***必须调用一下super***,它会执行基类的构造函数 在子类constructor
构造函数调用this以前必须先调用super
class FClass {
name: string = '超类'
constructor(name: string) { this.name = name; }
setAge(age:number) {
console.log(`${this.name} is ${age}`);
}
}
class SClass extends FClass {
constructor(name:string) {
super(name)
}
}
let f = new SClass('zzc')
f.setAge(20)
复制代码
public
能够被除本身以外的因此子类访问到 ts中全部成员默认为public
当一个成员被标记成private
时,没法被外部访问 若是类的constructor
声明成了private
那就没办法new
和继承了。protected
受保护的,跟private
基本同样,但它在子类中能够访问;
class FClass {
name: string = '超类'
private constructor(name: string) { this.name = name; }
setAge(age:number) {
console.log(`${this.name} is ${age}`);
}
}
let f = new FClass() // error
class Son extends FClass {} // error
复制代码
protected 受保护的例子
class F {
protected name: string;
constructor(name: string) { this.name = name; }
setAge(age:number) {
console.log(`${this.name} is ${age}`);
}
}
class Son extends F {
constructor() {
super('son')
}
getName() {
super.name // 能够在子类访问
}
}
let f = new F('super class')
let s = new Son()
console.log(s.name); // 不能在外部访问
console.log(s.getName()); // 但能够经过子类的方法return出来获取到
复制代码
在类中或者
constructor
都还能够更改readonly
的值,但在外部就没法更改了。
class F {
readonly a:number = 8
constructor(age:number) {
this.a = age
}
}
let f = new F(9)
f.a = 10 // error 没法在外部更改
复制代码
在
constructor
用一句话定义并赋值一个属性 只要在参数前面加了访问限定符就能够直接给一个属性直接赋值readonly
protected
public
private
static
是私有的,因此不能加。
class F {
readonly a:number = 8
constructor(readonly b:number) {
b = 10
}
}
let f = new F(9)
console.log(f); // {a,b}
复制代码
当一个属性只有get方法的时候,它就是只读的。 这也是一种外部改变静态属性的方法
// 当a = 'a' 时,内部的_a才会等于赋的值,不然报错。
class F {
private _a:string;
get a():string {
return this._a
}
set a(newA:string) {
if(newA === 'a') {
this._a = newA
}
else {
this._a = 'error'
}
}
}
let f = new F()
f.a = 'b'
console.log(f);
复制代码
它只挂在class自己,而不是经过new实例化后出来的对象 因此你能够经过
类.static属性
来调用,但不能用this
class F {
static num: number;
changeStatic() {
F.num = 19;
}
constructor () {
this.changeStatic()
console.log(F.num);
}
}
let f = new F();
复制代码
abstract
用来定义抽象类 和 在抽象类中定义抽象方法的 抽象类就是派生类的一个模板类,通常不会把它实例化,只是给子类继承用的。
abstract class Animal { // 抽象一个Animal类
abstract makeSound(): void; // 抽象一个方法,必须在子类实现它
move(): void {
console.log('roaming the earch...');
}
constructor () {
}
}
class Son extends Animal {
constructor() {
super()
}
makeSound() { // 必须实现抽象类中的方法
return false
}
haha() {
console.log('error');
}
}
let s:Animal // 能够指定抽象类为一个类型
s.haha() // 若是上面的声明了,那么调用抽象类中不存在的haha方法是不容许
s = new Animal() // 不能够new 抽象类
s = new Son() // 正确
s.makeSound() // 正确
复制代码
若是没有明确的指定类型,那么 TypeScript 会依照类型推论(Type Inference)的规则推断出一个类型。
let myFavoriteNumber = 'seven';
myFavoriteNumber = 7; // error
// 等价于 ==>
let myFavoriteNumber: string = 'seven';
myFavoriteNumber = 7;
复制代码
若是定义的时候没有赋值,无论以后有没有赋值,都会被推断成 any 类型而彻底不被类型检查:
let myFavoriteNumber;
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;
复制代码
表示变量能够是多种类型其中的一种
经过
|
分隔
let numOrStr : string | number;
numOrStr = 7;
numOrStr = '7';
numOrStr = true; // false
复制代码
当调用联合类型的方法时,只能调用俩类型中共有的方法。
let numOrStr : string | number;
numOrStr.length // 报错 length 不是 number 的方法
numOrStr.toString() // 能够
复制代码