内容简介:TypeScript 是 JavaScript 的类型的超集,它可以编译成纯 JavaScript。编译出来的 JavaScript 可以运行在任何浏览器上。TypeScript 编译工具可以运行在任何服务器和任何系统上。TypeScript 是开源的。以下内容均出自于TS入门教程以及Ts 官网的一些内容,没有基础的小伙伴直接看打了:star:️的内容即可。
TypeScript 是 JavaScript 的类型的超集,它可以编译成纯 JavaScript。编译出来的 JavaScript 可以运行在任何浏览器上。TypeScript 编译 工具 可以运行在任何服务器和任何系统上。TypeScript 是开源的。
以下内容均出自于TS入门教程
以及Ts 官网的一些内容,没有基础的小伙伴直接看打了:star:️的内容即可。
尝试
看了之后怎么搭个环境写一写?
mkdir demo cd demo touch 1.ts 复制代码
打开vscode,打开控制台,切换到问题 tab
欧了,开始尝试 ts 吧
:star:️基础类型
└boolean
let isDone: boolean = false; // 使用构造函数 Boolean 创造的对象不是布尔值 复制代码
└null & undefined
是所有类型的子类型
void类型不能赋值给 number
let u: undefined = undefined; let n: null = null; let num: number = undefined; let u: undefined; let num: number = u; 复制代码
└void 类型
一般表示函数没有返回值。用在变量上没有什么卵用。
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 复制代码
└数字 number
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; 复制代码
└字符串 string
单双引 ''
""
,模板字符的都被视为字符串
let str:string = '' 复制代码
:star:️数组类型
有多种声明数组的方式
-
最简单的方法是使用
类型 + []来表示数组:
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];
复制代码
- any 类型数组
let list:any[] = [1,"z",{}]
复制代码
- 元组类型声明
// 表示一个确定数组长度和类型的写法 const arr:[string,number] = ['2',3] 复制代码
- 类数组
就是伪数组的定义
官方已给了各自的定义接口 Arguments
, NodeList
, HTMLCollection
function sum() {
let args: IArguments = arguments;
}
复制代码
枚举 enum
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
复制代码
:star:️any 类型(任意值)
指代所有的类型
let a: any = '123' let a = 123; // 不声明默认 any 复制代码
never
表示永远不存在的值,一般会用来写抛出异常或推断为返回值为never的函数。(比如return一个其他的never类型)
function error(message: string): never {
throw new Error(message);
}
error('a')
复制代码
object 类型
非简单类型 也就是除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
复制代码
:star:️interface 接口
在 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' ,
}
复制代码
└只读属性 readonly
相当于是常量了,初次赋值后不能重新赋值
做为变量使用的话用 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
通过 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> // 但是可以用断言重写 复制代码
:star:️函数类型
常见的函数声明方式有: 函数声明 & 函数表达式
用 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
使用…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 会优先从最前面的函数定义开始匹配,所以多个函数定义如果有包含关系,需要优先把精确的定义写在前面。
:star:️类型断言
类型断言(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
}
复制代码
:star:️declare 声明
第三方库会暴露出一个变量,让我们在项目中直接使用。
但是 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 包的声明文件可能存在于两个地方:
-
跟 npm 包绑在一起,
package.json中有type字段,或者有index.d.ts声明文件。一般常用的包都有了,自己要发布 npm 包的时候最好也绑定在一起。 -
发布到了@types]( microsoft.github.io/TypeSearch/
) 里,一般这种情况是作者没写,其他人写了发上去的。
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 社区更加繁荣。
- export
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'
}
}
复制代码
-
混用
declareexport
通过 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;
}
复制代码
:star:️内置对象
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出来获取到
复制代码
在类中使用readonly
在类中或者 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}
复制代码
存取器 getter/setters
当一个属性只有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);
复制代码
静态属性 static
它只挂在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
用来定义抽象类 和 在抽象类中定义抽象方法的
抽象类就是派生类的一个模板类,一般不会把它实例化,只是给子类继承用的。
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() // 正确
复制代码
:star:类型推论
如果没有明确的指定类型,那么 TypeScript 会依照类型推论(Type Inference)的规则推断出一个类型。
let myFavoriteNumber = 'seven'; myFavoriteNumber = 7; // error // 等价于 ==> let myFavoriteNumber: string = 'seven'; myFavoriteNumber = 7; 复制代码
如果定义的时候没有赋值,不管之后有没有赋值,都会被推断成 any 类型而完全不被类型检查:
let myFavoriteNumber; myFavoriteNumber = 'seven'; myFavoriteNumber = 7; 复制代码
:star:️联合类型
表示变量可以是多种类型其中的一种
通过 |
分隔
let numOrStr : string | number; numOrStr = 7; numOrStr = '7'; numOrStr = true; // false 复制代码
当调用联合类型的方法时,只能调用俩类型中共有的方法。
let numOrStr : string | number; numOrStr.length // 报错 length 不是 number 的方法 numOrStr.toString() // 可以 复制代码
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Algorithms Unlocked
Thomas H. Cormen / The MIT Press / 2013-3-1 / USD 25.00
Have you ever wondered how your GPS can find the fastest way to your destination, selecting one route from seemingly countless possibilities in mere seconds? How your credit card account number is pro......一起来看看 《Algorithms Unlocked》 这本书的介绍吧!