ts深入理解笔记

  • 一个示例用法: some/folder/**/* 意味着匹配所有的文件夹和所有文件(扩展名为.ts/.tsx,当开启了allowJs: true选择时,扩展名可以是.js/.jsx)

声明空间


  • 在ts中存在两种声明空间:类型声明空间和变量声明空间

  • 类型声明空间,用来当作类型注解的内容,如在所示:

class Foo {};

interface Bar {};

type Bas = {};

  • 变量声明空间,变量声明空间包含可用作变量的内容。如上面所示的class Foo提供了一个类型的Foo到类型声明空间,此外它同样提供了一个变量Foo到变量声明空间当中,如下所示:

class Foo {}

const someVar = Foo;

const someOthervar = 123;

模块

  • 全局模块,在默认情况下,当开始在一个新的TypeScript文件中写下代码时,其处于全局命名空间中。这种方式容易造成文件内的代码命名冲突。推荐使用文件模块

  • 文件模块,又被称之为外部模块。如果在ts文件的根级别位置含有import或者export,那么它会在这个文件中创建一个本地的作用域,如下所示:

//文件定义在foo.js文件中

export const foo = 123;

//文件定义在一个文件中

import { foo } from ‘./foo’;

const bar = foo;

文件模块详情
  • 文件模块拥有强大的功能和较强的可用性
commonjs、 amd 、 es modules 、 others
  • 首先需要明白关于这些模块系统的不一致性。需要根据不同的module选项来把TypeScript编译成不同的js模块类型。

  • 如何书写TypeScript模块

  • 放弃使用import/require语法即import foo = require(‘foo’)写法

  • 推荐使用ES模块语法

  • ES5模块语法

  • 使用export关键字导出一个变量或类型

export const someVar = 123;

export type someType = {

foo: stringl

}

  • export的写法除了上述的写法,还有下面这种

const someVar = 123;

type someType = {

type: string;

}

export { someVar someType }

  • 采用重命名变量的方式导出

const someVar = 123;

export { someVar as aDifferentName };

  • 使用import关键字导入一个变量或者是一个类型

import { someVar, someType } from ‘./foo’;

  • 通过重命令的方式导入变量或者类型:

import { someVar as aDifferentName } from ‘./foo’;

  • 除了指定加载某个输出值,还可以使用整体加载,即用星号(*)指定一个对象,所有输出都加载在这个对象上面

import * as foo from ‘./foo’;

//此时可以使用’foo.someVar’和‘foo.someType’以及其他任何从‘foo’到处的变量或者类型

  • 只导入模块

import ‘core-js’;

  • 从其他模块导入后,整体导出

export * from ‘./foo’;

  • 从其他模块导入后,部分导出:

export { someVar } from ‘./foo’;

  • 通过重命名,部分导出从另一个模块导入的项目:

export { someVar as aDifferentName } from ‘./foo’;

  • 默认导入/导出

  • 使用export default

  • 在一个变量之前

  • 在一个函数之前

  • 在一个类之前

  • 导入使用import someName from 'someModule’的语法

  • 模块路径,存在两种截然不同的模块:

  • 相对模块路径,通过路径的方式来查找

  • 其他动态查找模块:(如: core-js, typestyle, react或者是react/core)

  • 相对模块路径

  • 如果文件 bar.ts 中含有 import * as foo from ‘./foo’,那么 foo 文件必须与 bar.ts 文件存在于相同的文件夹下

  • 如果文件 bar.ts 中含有 import * as foo from ‘…/foo’,那么 foo 文件所存在的地方必须是 bar.ts 的上一级目录;

  • 如果文件 bar.ts 中含有 import * as foo from ‘…/someFolder/foo’,那么 foo 文件所在的文件夹 someFolder 必须与 bar.ts 文件所在文件夹在相同的目录下。

  • 动态查找,当导入路径不是相对路径时,模块解析将会模仿Node模块解析器,如:

  • 当使用import * as foo from ‘foo’,将会按照如下的顺序查找模块:

  • ./node_modules/foo

  • …/node_modules/foo

  • …/…/node_modules/foo

  • 直到根目录

  • 当使用import * as foo from ‘something/foo’,将会按照下面的这种方式去查询

  • ./node_modules/something/foo

  • …/node_modules/something/foo

  • …/…/node_modules/something/foo

  • 直到根目录

  • 重写类型的动态查找

  • 在项目中,可以通过declare module ‘somePath’ 声明一个全局模块的方式,来解决查找模块路径的问题,如下所示:

declare module ‘foo’ {

export var bar: number;

}

  • 接着引入该模块的内容

import * as foo from ‘foo’;

  • import/require仅仅是导入类型,如下所示

import foo = require(‘foo’);

  • 实际上,上面的语法表达两层含义:

  • 导入foo模块的所有类型信息

  • 确定foo模块运行时的依赖关系

  • 使用例子:懒加载

  • 类型推断需要提前完成,这就意味着,如果想要在bar文件里,使用从其他文件foo导出的类型,将采用下面的方式来处理:

import foo = require(‘foo’);

let bar: foo.someType;

  • 在某些场景下,只想在需要加载模块foo,此时需要尽在类型注解中使用导入的模块名称,而不是在变量中使用。在编译js时,这些将被移除,接着需要手动导入需要的模块,如下所示:

import foo = require(‘foo’);

export function loadFoo() {

//懒加载foo,原始的加载仅仅用来做类型注解

const _foo: tyoeof foo = require(‘foo’);

}

import foo = require(‘foo’);

export function loadFoo() {

require([‘foo’], (_foo: typeof foo) => {

//使用’_foo’替代’foo’来作为变量使用

})

}

  • 上述的这些场景通常在以下的情况使用:

  • 在web app中,当在特定路由上加载js时

  • 在node应用中,只想加载特定模块,用来加快启动速度

  • 使用例子:打破循环依赖

  • 类似于懒加载的使用用例,某些模块加载器(commonjs/node和amd/requires)不能很好的处理循环依赖。在这种情况下,一方面使用延迟加载代码,另一方面预先加载模块

  • 使用例子:确保输入

  • 当加载一个模块,只想引入其附加作用时,如果仅仅是import/require(导入)一些并没有与你的模块或者模块家在器有任何依赖的js代码,在经过ts编译后,这些将会被完全忽视。在这种情况下,可以使用一个ensureImport变量,来确保编译的js依赖与模块。如下所示:

import foo = require(‘./foo’);

import bar = require(‘./bar’);

import bas = require(‘./bas’);

const ensureImport: any = foo || bar || bas

  • global.d.ts

  • 该文件,用来将一些接口或者类型放在全局命名空间里,这些定义的接口和类型能在你的所有ts代码里使用

命名空间

(function(something) {

smoething.foo = 123;

})(something || (something = {} ))

  • 在上面的实例中something || (something = {}) 允许匿名函数function (something) {} 向现有对象添加内容,或者创建一个新对象,然后向该对象添加内容。

let something;

(function(something) {

something.foo = 123;

})(something || (something = {}));

console.log(something);

// { foo: 123 }

(function(something) {

something.bar = 456;

})(something || (something = {}));

console.log(something); // { foo: 123, bar: 456 }

  • 在确保创建的变量不会泄露至全局命名空间时,这种方式在js中很常见,当基于文件模块使用时,无须担心这点,该模式仍然适用于一组函数的逻辑分组。因此在ts中提供了namespace关键字来描述这种分组,如下所示:

namespace Utility {

export function log(msg) {

console.log(msg);

}

export function error(msg) {

console.log(msg);

}

}

//usage

Utility.log(‘Call me’);

Utility.log(‘maybe’);

  • ts经过编译后js代码如下所示:

(function(Utility){

//…

})(Utility || (Utility = {}))

动态导入表达式

  • 其是es的一个新功能,允许你在程序的任意位置异步加载一个模块,TC39js委员会有一个提按,目前正处于第四阶段,它被称之为import () proposal for JavaScript

  • 此外webpack bundle有一个Code Splitting 功能,它允许你将代码块拆分为多个许多块,这些块在将来可被异步下载。因此,可以在程序中首先提供一个最小的程序启动包,并在将在异步加载其他模块

  • webpack实现代码分割的方式有两种:使用import() 和 require.ensure()(最后考虑,webpack具体实现)。因此期望ts的输出是保留import语句,而不是将其转化为其他任何代码

  • 在下面的这个例子中,演示了如何配置webpack和TypeScript2.4+,如下所示:

//webpack加载方式

import(/* webpackChunkName: “momentjs” */ ‘moment’)

.then(moment => {

//懒加载的模块拥有所有类型,并且能按期工作

//类型检查会工作,代码引用也会工作 :100:

const time = moment().format();

console.log(‘Typescript >= 2.4.0 Dynamic Import Expression:’);

console.log(time);

})

.catch(err => console.log(“Failed to load moment”,err))

//tsconfig.json配置文件

{

“compilerOptions”: {

“target”: “es5”,

“module”: “esnext”,

“lib”: [

“dom”,

“es5”,

“scripthost”,

“es2015.promise”

],

“jsx”: “react”,

“declaration”: false,

“sourceMap”: true,

“outDir”: “./dist/js”,

“strict”: true,

“moduleResolution”: “node”,

“typeRoots”: [

“./node_modules/@types”

],

“types”: [

“node”,

“react”,

“react-dom”

]

}

}

Typescript类型系统


  • ts类型系统的主要功能,以下为一些关键点

  • ts类型系统被设计为可选的

  • ts不会阻止js的运行,即使存在类型错误也不例外,这能让js逐步迁移至ts

  • ts中的一些数据类型

从js中迁移至ts包括以下步骤

  • 从js迁移到ts的步骤如下:

  • 添加一个tsconfig.json文件

  • 把文件扩展名.js改为.ts,开始使用any来减少错误

  • 开始在ts中写代码,尽可能的减少any的使用

  • 回到旧代码,开始添加类型注解,并修复已识别的错误

  • 为第三方js代码定义环境声明

  • 减少错误,代码迁移至ts后,ts将立即对代码进行类型检查,此时可以采用any来解决大部分的报错问题

  • 第三方代码,可以将js代码改成ts代码,但是不能全部都使用ts代码,这正是ts环境声明支持的地方,可以专门使用一个文件作为开始,然后向文件里添加东西。或者可以创建一个针对于特定库的声明文件

  • 第三方的npm模块,与全局变量声明类似,可以快速定义一个全局模块,如jq,如果你想把它作为一个模块来使用时,可以自己通过以下的方式来实现:

declare module ‘jquery’;

import * as $ from ‘jquery’;

  • 额外的非js资源,在ts中,甚至可以允许导入任何文件,例如.css文件(如果使用webpack样式加载器或css模块),只需要添加如下代码(放在global.d.ts):

declare module ‘*.css’;

// 声明之后可以使用import * as foo from ‘./some/file.css’

@types

  • 使用types,可以通过npm来安装使用@types,例如:jquery添加声明文件,如下所示

npm install @types/jquery

  • @types支持全局和模块类型定义

  • 全局@types,默认情况下,typescript会自动包含支持全局使用的任何声明定义。例如,对于jq,你应该能够在全局使用$符号

  • 模块@types,安装完成之后,不需要特别的配置,就可以像模块一样使用它

import * as $ from ‘jquery’;

  • 控制全局,对于一些团队而言,拥有允许全局使用的定义是一个问题。因此可以通过配置tsconfig.json的compilerOptions.types选择,引入有意义的类型,如下所示:

{

“compilerOptions”: {

“types”: [

“jquery”

]

}

}

环境声明

  • 环境声明允许你安全的使用现有的js库,并且能让你的js,CoffeeScript或者其他需要编译成js的语言逐步迁移至Typescript中

  • 声明文件,可以通过declare关键字来告诉typescript,你正在试图表述一个其他地方已经存在的代码。如:写在js CoffeeScript或者是像浏览器和Node.js运行环境里的代码,如下所示:

foo = 123;

declare var foo: any;

foo = 123;

  • 你可以选择把这些声明放在.ts或者.d.ts里,在实际项目里,强烈建议你应该把声明放在独立的.d.ts里。如果一个文件有扩展名.d.ts,这意味着每个根级别的声明都必须以declare关键字作为前缀。这有利于让开发者清楚知道,

在这里ts将不会把它编译成任何代码,同时开发者需要确保这些在编译时存在

  • 变量,当想告诉ts编译器关于process变量时,可以采用下面的这种方式

declare let process: any’;

//允许你使用process,并能成功通过typescript的编译:

process.exit();

//推荐尽可能的使用接口,例如:

interface Process {

exit(code?: number): void;

}

declare let process: Process;

接口

  • 接口的声明方式

interface Point {

x: number;

y: number;

}

  • 使用类实现接口,首先使用interface定义接口,之后使用implements关键字来确保兼容性,如下所示:

interface Point {

x: number;

y: number;

}

class Point implements Point {

x: number;

y: number;

}

  • 并不是每一个接口都是很容易实现的,接口旨在声明js中可能存在的任意结构,思考下面的demo

interface Crazy {

new(): {

hello: number;

}

}

class CrazyClass implements Crazy {

constructor() {

return {

hello: 123

}

}

}

枚举

  • 组织收集有关联变量的一种方式,定义的方式是采用enum关键字,如下所示:

enum CardSuit {

Clubs,

Diamonds,

Hearts,

Spades

}

  • 数字类型枚举和数字类型。数字类型枚举,允许将数字类型或者其他任何与数字类型兼容的类型赋值给枚举类型的实例

  • 数字类型枚举与字符串类型,从下面的例子中看出,你可以使用Tristate变量来把字符串枚举类型改造成一个数字或者是数字类型的枚举类型

enum Tristate {

False,

True,

Unkown

}

//被编译成js代码后

var Tristate;

(function(Tristate){

Tristate[(Tristate[‘False’] = 0)] = ‘False’;

Tristate[(Tristate[‘True’] = 1)] = ‘True’;

Tristate[(Tristate[‘Unkonw’] = 2)] = ‘Unkonw’;

})(Tristate || (Tristate = {}));

  • 改变与数字枚举关联的数字,默认情况下,第一个枚举值是0,然后每个后续值以此递增。也可以通过特定的赋值来改变给任何枚举成员关联的数字。可以将第一个枚举值设置为任意数值,后续会依次增加

  • 使用数字类型作为标志,枚举很好用途是用于作为标志。这些标志允许你检查一组条件中的某个条件是否为真。如下所示:

enum AnimalFlags {

None = 0,

HasClaws = 1 << 0,

CanFly = 1 << 1,

EatsFish = 1 << 2,

Endangered = 1 << 3,

}

//在这里使用左移符号,将数字1的二进制向左移动位置得到数字0001,0010,0100, 1000

  • 字符串枚举,枚举类型的值为字符串类型,如下所示:

export enum EvidenceTypeEnum {

UNKNOWN = ‘’,

PASSPORT_VISA = ‘passport_visa’,

PASSPORT = ‘passport’,

SIGHTED_STUDENT_CARD = ‘sighted_tertiary_edu_id’,

SIGHTED_KEYPASS_CARD = ‘sighted_keypass_card’,

SIGHTED_PROOF_OF_AGE_CARD = ‘sighted_proof_of_age_card’

}

lib.d.ts

  • 当你安装typescript时,会顺带安装一个lib.d.ts声明文件。这个文件包含js运行时以及DOM中存在各种常见的环境声明

  • 自动包含在Typescript项目的编译上下文中;

  • 它能让你快速开始书写经过类型检查的js代码;

  • 你可以指定 --noLib的编译命令行标志(或者在tsconfig.json中指定选项noLib: true)

  • 使用例子,如下所示

const foo = 123;

const bar = foo.toString();

//这段代码的类型检查正常,因为lib.d.ts为所有js对象定义了toString方法,如果在noLi选项下,使用相同的代码,这将会出现类型检查错误

  • 观察lib.d.ts的内容,其主要是一些变量声明(如:window、document、math)和一些类似的接口声明(如:Window、Document、Math),寻找代码类型(如:Math.floor)的最简单方式是使用IDE的F12(跳转定义),如下面的window接口定义

interface Window

extends EventTarget,

WindowTimers,

WindowSessionStorage,

WindowLocalStorage,

WindowConsole,

GlobalEventHandlers,

IDBEnvironment,

WindowBase64 {

animationStartTime: number;

applicationCache: ApplicationCache;

clientInformation: Navigator;

closed: boolean;

crypto: Crypto;

// so on and so forth…

}

  • 修改原始类型,在ts中,接口是开发式的,这意味着当你想使用不存在的成员时,只需将它们添加至lib.d.ts中的接口声明即可。TypeScript将会自动接收它。注意当需要在全局模块中做这些修改,以使这些接口与lib.d.ts相关联。推荐创建一个global.d.ts的特殊文件

  • Window,仅仅是添加至window接口:

interface Window {

helloword(): void;

}

//将允许你以类型安全的形式使用它

window.helloWorld = () => console.log(‘hello world’);

// Call it

window.helloWorld();

// 滥用会导致错误

window.helloWorld(‘gracius’); // Error: 提供的参数与目标不匹配

  • 编译目标对lib.d.ts的影响,设置编译目标为es6时,能导致lib.d.ts包含更多像Promise现代内容的环境声明。

  • –lib选项,使用命令行的方式添加运行时的编译环境,如下所示:

tsc --target es5 --lib dom,es6

可调用的

  • 可以使用类型别名或者接口来表示一个可被调用的类型注解:

interface ReturnString {

(): string;

}

//可以表示一个返回值为string的函数:

declare const foo: ReturnString;

const bar = foo();

  • 箭头函数,指定可调用的类型签名更容易,typescript运行你使用简单的简单函数类型注解,例如,在一个以number类型为参数,以string类型为返回值的函数中,写法如下:

const simple: (foo: number) => string = foo => foo.toString();

  • 可实例化,仅仅是可调用的一种特殊情况,使用new作为前缀,如下所示:

interface CallMeWithNewToGetString {

new (): string;

}

// 使用

declare const Foo: CallMeWithNewToGetString;

const bar = new Foo(); // bar 被推断为 string 类型

类型断言

  • ts允许覆盖它的推断,并且能以你任何想要的方式分析它,这种机制被称为类型断言,ts类型断言用来告诉编译器你比他更了解这个类型,并且它不应该发生错误

  • 使用as foo来为类型断言

  • 类型断言与类型转化,类型转化通常意味桌某种运行时支持。但是类型断言纯粹是一个编译时语法,同时也是一种为编译器提供关于如何分析代码的方法

  • ts是怎样确定单个断言是否足够,当S类型是T类型的子集,或者T类型是S类型的子集时,s能够被断言成T

freshness

  • 为了能让检查对象字面量类型更容易,ts提供了freshness的概念(它也被称之为更严格的对象字面量检查)用来确保对象字面量在结构上类型兼容,如下所示:

function logName(something: { name: string }) {

console.log(something.name);

}

logName({ name: ‘matt’ }); // ok

logName({ name: ‘matt’, job: ‘being awesome’ }); // Error: 对象字面量只能指定已知属性,job 属性在这里并不存在。

  • 允许额外的属性,一个类型能够包含索引签名,以明确表明可以使用额外的属性,如下所示:

let: x : { foo : number, [x: string]: any };

x = { foo: 1, baz: 2 }

类型保护

  • typeof,判断一个变量的简单数据类型

  • instanceof,用来看两个对象之间是否具有继承关系

  • in用来判断一个对象上的属性是否具有可枚举性

  • 字面量类型保护,当在联合类型里使用字面量时,可以检查它们是否有区别:

type Foo = {

kind: ‘foo’; // 字面量类型

foo: number;

};

type Bar = {

kind: ‘bar’; // 字面量类型

bar: number;

};

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
型是S类型的子集时,s能够被断言成T

freshness

  • 为了能让检查对象字面量类型更容易,ts提供了freshness的概念(它也被称之为更严格的对象字面量检查)用来确保对象字面量在结构上类型兼容,如下所示:

function logName(something: { name: string }) {

console.log(something.name);

}

logName({ name: ‘matt’ }); // ok

logName({ name: ‘matt’, job: ‘being awesome’ }); // Error: 对象字面量只能指定已知属性,job 属性在这里并不存在。

  • 允许额外的属性,一个类型能够包含索引签名,以明确表明可以使用额外的属性,如下所示:

let: x : { foo : number, [x: string]: any };

x = { foo: 1, baz: 2 }

类型保护

  • typeof,判断一个变量的简单数据类型

  • instanceof,用来看两个对象之间是否具有继承关系

  • in用来判断一个对象上的属性是否具有可枚举性

  • 字面量类型保护,当在联合类型里使用字面量时,可以检查它们是否有区别:

type Foo = {

kind: ‘foo’; // 字面量类型

foo: number;

};

type Bar = {

kind: ‘bar’; // 字面量类型

bar: number;

};

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-2cI56fyr-1715623284324)]

[外链图片转存中…(img-25lmx7qH-1715623284325)]

[外链图片转存中…(img-14kk5QC3-1715623284325)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值