Typescript

Typescript学习笔记。From zero to hero。 1. Array,Tuple,Union,Enum // Basic Types let id: number = 5 //变量后面加类型,用冒号隔开 let company: string = 'Traversy Media' let isPublished: boolean = true let x: any = 'Hello' //any类型变量,可以放任何类型数据 let ids: number[] = [1, 2, 3, 4, 5] //不定长数组 区别传统静态语言的int a[4]; let arr: any[] = [1, true, 'Hello'] //any数组,可以混合各种值 // Tuple let person: [number, string, boolean] = [1, 'Brad', true] //元组:已知元素数量和类型的数组,各元素的类型不必相同。 // Tuple Array let employees: [number, string][] //每个元素是元组的数组 employee = [ [1, 'Brad'], [2, 'John'], [3, 'Jill'], ] // Union let pid: string | number //联合 /* 以下是C语言的联合,都是一个变量可以存几种不同类型数据 union data{ int n; char ch; double f; }; */ pid = '22' // Enum enum Direction1 { Up = 1, Down, Left, Right, } enum Direction2 { Up = 'Up', Down = 'Down', Left = 'Left', Right = 'Right', } 2. Map //定义 type MapType = { [id: string]: string; } //实例化 const map: MapType = {}; map['a'] = 'b'; map['c'] = 'd'; //删除 delete map['c']; //枚举 for (let i in map) { console.log(map[i]); } //得到包含所有key的数组 console.log(Object.keys(map)); //另外一种用Record的实现方式 const map: Record<string, string> = {}; map['a'] = 'b'; map['c'] = 'd'; 3. Object // Objects type User = { id: number name: string } const user: User = { id: 1, name: 'John', } // Type Assertion 类型的选择 let cid: any = 1 // let customerId = <number>cid 从any到 number 的选择 let customerId = cid as number 4. Function // Functions function addNum(x: number, y: number): number { return x + y } // Void function log(message: string | number): void { console.log(message) } 5. Interface, Class // Interfaces interface UserInterface { readonly id: number name: string age?: number } const user1: UserInterface = { id: 1, name: 'John', } interface MathFunc { (x: number, y: number): number } const add: MathFunc = (x: number, y: number): number => x + y const sub: MathFunc = (x: number, y: number): number => x - y interface PersonInterface { id: number name: string register(): string } // Classes class Person implements PersonInterface { id: number name: string constructor(id: number, name: string) { this.id = id this.name = name } register() { return `${this.name} is now registered` } } const brad = new Person(1, 'Brad Traversy') const mike = new Person(2, 'Mike Jordan') // Subclasses class Employee extends Person { position: string constructor(id: number, name: string, position: string) { super(id, name) this.position = position } } const emp = new Employee(3, 'Shawn', 'Developer') 6. Generics // Generics => C++的泛型 function getArray<T>(items: T[]): T[] { return new Array().concat(items) } let numArray = getArray<number>([1, 2, 3, 4]) let strArray = getArray<string>(['brad', 'John', 'Jill']) strArray.push(1) // Throws error 7. FAQ 7.1. type alias or interface https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#differences-between-type-aliases-and-interfaces ...

2022年3月4日 · 3 分钟 · 费小慢

Typescript中的模块

和python不同, Typescript/Javascript模块导入导出方式五花八门。 目前因为ES6已经标准化,推荐用import/export。 历史遗留的require/exports由于存在数目巨大的npm库,也还将长期共存。 1. ES6模块导入导出 1.1. 语法 三种export方式,两种import方式 export import export var; import {var} from module export {var}; import {var} from module export default var import var from module 另外可以用 import * as module_alias from module 导入模块所有export的变量到一个对象 module_alias 中。 1.2. 例子 1.2.1. testEs6Export.ts 'use strict' //导出变量 export const a = '100'; //导出方法 export const dogSay = function(){ console.log('wang wang'); } //导出方法第二种 function catSay(){ console.log('miao miao'); } export { catSay }; //export default导出 const m = 100; export default m; //export defult const m = 100;// 这里不能写这种格式。 1.2.2. index.ts import { dogSay, catSay } from './testEs6Export'; //导出了 export 方法 import m from './testEs6Export'; //导出了 export default import * as testModule from './testEs6Export'; //as 集合成对象导出 2. CommonJS 模块导入导出 2.1. 语法 模块中有module.export对象,里面存放的就是export的变量。 ...

2022年3月4日 · 1 分钟 · 费小慢

基点 bps 是什么

bps是什么? bp(或者bps)是Basis Point的简称。 中文翻译成基点。 是一个比例, 万分之一的意思。 也就是1%的百分之一。 为什么要有这个概念 为什么不直接说万分之一,而引入一个新术语? Basis points are convenient and steady. Basis points are less ambiguous than percentages as they represent an absolute, set figure instead of a ratio. For example, a 1 percent increase on a 5 percent interest rate could be interpreted as either 5.05 percent or 6 percent. Conversely, if the rate increases by 100 basis points, the result is constant. The rate updates to 6 percent. ...

2022年3月3日 · 1 分钟 · 费小慢

设计高性能可扩展的API服务

Directus本身是NodeJS实现的API服务器。 其能支持多大的TPS取决于数据库系统,API查询设计等多个因素。本文提供一些架构上的考虑,目的是使得Directus应用能随着业务的增长,通过增加硬件等方式,同步提高系统处理能力,在性能上具有好的扩展性。 1. 如何规划可扩展的Directus应用 Directus 对于高负载情况的处理有以下两个关注点: 横向扩展应用服务器(directus实例多部署几个) 数据库服务器采用高性能方案,例如Amazon Aurora或者CockroachDB Rijk van Zanten: That being said, I do highly recommend horizontally scaling your Directus instance if you’re planning on running it at scale. Make sure you use Redis for caches / rate limiter, and S3 or another shared file storage for the file storage. At that point, the bottleneck will become the amount of allowed connections and the overall server performance of the database. That being said, there’s a lot of database services nowadays that scale virtually endless, like Amazon Aurora or CockroachDB. ...

2022年3月2日 · 1 分钟 · 费小慢

OAuth2 应用实践:Directus集成钉钉登录的尝试

1. 项目简介 这个小项目预期结果是让 Directus 支持使用钉钉账号来登录。 在了解OAuth2协议后(参见上一篇blog,参考资料1),已经有足够知识储备来实施。 Directus 原生支持使用GitHub登录, 所以,解决思路是先从GitHub入手。按下面步骤进行: 配置Directus使用GitHub账号登录,熟悉Directus对OAuth的标准支持功能 配置Directus使用钉钉账号登录,由于钉钉的协议实现和RFC6749/GitHub有不同,这里有可能需要见招拆招 上线Directus到服务器环境,在钉钉的PC版和手机版验证 2. 环境配置 在本地用ngrok暴漏出一个服务,来接受OAuth服务器的redirect。 ngrok http 8055 得到 https://445a-240e-47c-30b0-3b10-600e-ea25-cde5-2334.ngrok.io/ 作为外网域名来访问本机8055端口的directus。 3. Directus 使用 GitHub账号登录 按参考资料2中配置参数。以下配置中,对每一个新的GitHub授权用户,Directus在登录过程中会使用用户email自动创建一个Directus用户,并且将其角色赋值为AUTH_GITHUB_DEFAULT_ROLE_ID。 AUTH_PROVIDERS="github" AUTH_GITHUB_DRIVER="oauth2" AUTH_GITHUB_CLIENT_ID="7e....ae" AUTH_GITHUB_CLIENT_SECRET="d5.......d9" AUTH_GITHUB_AUTHORIZE_URL="https://github.com/login/oauth/authorize" AUTH_GITHUB_ACCESS_URL="https://github.com/login/oauth/access_token" AUTH_GITHUB_PROFILE_URL="https://api.github.com/user" AUTH_GITHUB_ALLOW_PUBLIC_REGISTRATION="true" AUTH_GITHUB_DEFAULT_ROLE_ID="0f5f1b5a-106f-4ec7-a8b8-6f11482a060f" AUTH_GITHUB_ICON="github" # AUTH_GITHUB_EMAIL_KEY="email" # AUTH_GITHUB_IDENTIFIER_KEY="email" 重启Directus让配置生效后,可以看到登录界面的GitHub选项。 选择授权后,成功登录Directus。 检查Directus中新生成的用户和权限正常。 4. Directus 使用钉钉账号登录的尝试 先照猫画虎配置下。 AUTH_PROVIDERS="github, dingtalk" ... ... AUTH_DINGTALK_DRIVER="oauth2" AUTH_DINGTALK_CLIENT_ID="ding...txt" AUTH_DINGTALK_CLIENT_SECRET="c6rCT...h4ohKlq5oz" AUTH_DINGTALK_AUTHORIZE_URL="https://login.dingtalk.com/oauth2/auth" AUTH_DINGTALK_ACCESS_URL="https://api.dingtalk.com/v1.0/oauth2/userAccessToken" AUTH_DINGTALK_PROFILE_URL="https://api.dingtalk.com/v1.0/contact/users/me" AUTH_DINGTALK_ALLOW_PUBLIC_REGISTRATION="true" AUTH_DINGTALK_DEFAULT_ROLE_ID="0f5f1b5a-106f-4ec7-a8b8-6f11482a060f" AUTH_DINGTALK_ICON="alipay" #AUTH_DINGTALK_EMAIL_KEY="email" #AUTH_DINGTALK_IDENTIFIER_KEY="email" 点击Log In with Dingtalk可以正常授权, 但授权后被redirect到了 /admin/login?reason=INVALID_USER 怀疑是钉钉重定向回来的链接没有code参数(参见上一篇协议解析,钉钉是用的authCode参数),第一时间先在社区开个issue看看有没有其他人碰到过。 同时,对oauth2的driver做了一个临时补丁, 当有authCode时候, 就把authCode赋值给code。 ...

2022年2月27日 · 2 分钟 · 费小慢

OAuth2 协议解析:以GitHub和钉钉为例

1. 原理 假设有一个APP,要我使用GitHub授权登录。 在这个登录场景中: 我作为数据的所有者告诉系统(GitHub),同意授权第三方应用(App)进入系统,获取某些数据(我的ID,头像等)。系统从而产生一个短期的进入令牌(token),用来代替密码,供第三方应用(APP)访问数据使用。 token是短期有效的,我可以随时通过GitHub把这个Token注销,从而使得APP不再能访问我的ID/头像等信息。 这里面有四个角色:用户,应用,系统,资源。 用户是资源所有者,应用是资源的使用者,系统是资源管理者。 现实生活中, 应用和系统各自有一个实例, 用户有多个实例。 应用和系统之间通过OAuth2协议通信。 用户在通信过程中参与(授权)。 把上面时序图对应到一个生活中的场景,业主授权快递公司出入小区送快递: Client Req Auth: 顺丰快递员打电话给业主,有你的快递,得送进去,给办个出入证吧 Resource Owner Grant Auth: 业主联系小区物业, 我是业主,这是我的证明,我允许顺丰最近两天可以进小区物业给我送快递 小区物业告诉业主,OK,你让顺丰联系我拿临时出入证,就说3号楼201房间,授权码:核酸检测利国利民 Client Sends Auth Grant 快递公司联系小区物业说,我是顺丰,这是我的证明,需要给3号楼201房间送快递,业主的授权码是核酸检测利国利民 Auth Server Sends Access Token 小区物业说好,授权码没问题,这个是临时出入证,两天有效。 Clients Sends Access Token 快递员用临时出入证开小区大门 Protected Resoure sends resource 快递员进入小区 注意: 第二步,业主联系物业时候, 要证明自己的确是业主(用小区app登录) 第三步,顺丰联系物业时候,需要证明自己的确是顺丰(报小区物业预先给顺丰分配的client_secret让物业核对). 物业还要检查3号楼201业主的确允许了(通过查授权码)。 如果快递员直接到小区门卫那里说 “核酸检测利国利民”,是没有作用的。“核酸检测利国利民”承载的信息是3号楼201业主允许顺丰在2天内进小区。 快递员给门卫报这个授权码没用,门卫只认出入证。再说,门卫也不知道快递员是不是顺丰的啊。这个授权码只有在快递公司把它换成临时出入证后才可以进小区。 换临时出入证需要验证顺丰的身份。“核酸检测利国利民”承载的信息是可以公开的,其它人听到了也不会有安全风险,因为其他人没有顺丰在物业处注册得到的client_secret, 没办法用授权码换临时出入证。 临时出入证需要妥善保管,任何人拿着都能进小区了。 OAuth2的过程是当有快递时候,业主授权小区物业给快递公司分配临时出入证,在一定的时间内可以出入小区。在同一时间,会存在很多个不同业主授权的有效临时出入证,但一个业主对一家快递公司,在同一时间只有一个有效临时出入证。 角色间的类比关系如下。 中文术语 时序图中的概念 GitHub授权登录APP场景 业主授权顺丰进小区场景 用户 Resource Owner GitHub用户 业主 应用 Client GitHub OAuth APP 顺丰 系统 Authorization Server GitHub 小区物业 资源 Protected Resource GitHub用户名称,头像 小区内部道路 授权 Authorization Grant code 核酸检测利国利民 令牌 Access Token token 临时出入证 下图出自RFC6750, 实际实现时候,Client是APP, Auth Server/Resource Server在同一个域名后面(GitHub,钉钉,Facebook…), Resource Owner也是在这个域名下完成授权(Auth Grant)。 ...

2022年2月26日 · 3 分钟 · 费小慢

在项目中引入细分领域的开源软件

1. 引言 开源软件已是信息社会的基石。 GitHub上软件种类众多,市场定位和成熟度也各不相同。 对于成熟的通用软件,例如Linux,NGINX等。将这类软件引入自研项目中不存在太多顾虑,因为通用所以用的人多。可以找到大量的成功案例,众多的开发维护人员。一旦碰到问题,也容易在网上搜到答案。 但还有一类开源软件, 定位相对没有那么通用,虽然其在细分领域已经做的比较好了,但由于生态环境较小,在项目中引入时候,还是有很多因素需要考虑: 现有的开发人员有能力掌握并承接后续开发吗? 如果使用中出现问题, 有没有合适的开发人员去解决?或者去做二次开发? 系统上线后,有没有合适的人员去长期维护? 对这类软件,在引入到项目中前,有两种办法增强信心: 找到合适的人 成为该软件开源社区的一员 2. 找到专家 有如下几个方法。 在软件的开发者中搜索是否有中国开发者,尝试建立联系。 百度该软件相关的讨论区,QQ群等。 加入并倾听,识别出专家。 寻找并参加该软件相关的培训。 寻找GitHub上该软件的相关软件。 例如引用该软件的软件,该软件的Plugin等等。并合作者建立联系。 以上的目的都是在现实世界建立起和专家的联系。 然后通过和专家的电话,邮件,吃饭喝茶等各种交流来进一步了解该软件在国内的生态情况,以及软件的优缺点。 为后续引入打好基础。 和专家建立起联系后, 后续也方便和专家进一步在项目上合作共赢。 3. 融入开源社区 如果国内找不到专家。 就只能撸胳膊上,让自己成为专家了。 3.1. 学习使用 主要看如下几方面的内容: 软件自身的官方文档 Youtube上的介绍视频,以及第三方教程 软件的单元测试 以上,结合大粒度的源码分析,可以掌握软件的大体脉络。 3.2. 和同行讨论 一般开源软件社区有如下几个地方。 GitHub项目Issues GitHub项目Discussions Discord或者Slack等讨论组 Email邮件组 通过加入并查看大家在讨论哪些问题,能了解到软件的一些使用细节和适用场景。 在学习的同时,也要多思考,多提问题, 多回答社区的问题。 回答社区的问题可以针对和自己项目相关的领域问题,积极回答问题有两点好处: 驱动自己认真深入思考,如果我在项目中碰到这个问题,该怎么处理? 和其它社区用户,以及软件开发维护者们建立联系 以上是 Headless CMS Directus 的项目讨论社区。 我最近在调研用Directus来做API中间件,watch了该项目的讨论区,GitHub会把所有讨论实时发到我一个特定的邮箱里面。 通过查看大家的讨论,有很多收获。我也把自己学到的经验总结提炼出来帮助其它用户, “Most Helpful”榜单排名第5的就是我。 3.3. 小规模MVP验证 在看的基础上,也需要动手做。 按照自己项目的特点,定义一个和该软件相关的最小MVP流程,尝试引入该开源软件实现。 以上三点,相辅相成,可以同步进行。 理想的结果是: 自己变成了专家,后续在项目中引入 研究过程中发现软件不适用,果断放弃

2022年2月25日 · 1 分钟 · 费小慢

自由泳抱水和推水的体会

用核心来推水 有两个陆上辅助练习: 滚动带动推水 前伸臂做出抱水姿势, 保持肘部和两肩成一条直线。 通过肩膀滚动,形成入水手一侧的伸展。 同时,抱水一侧尽可能长的保持肘部和两肩一条直线。 以上关注点是抱水肘部和两肩三点一线。 肘尖沿着两肩延长线向前上方顶出去,前臂自然下垂。 通过这个关注点,体会通过肩膀的滚动和后撤来完成推水。胳膊只关注稳定好抱水的形状,不要发力主动推。尤其是在前半程。后半程退肩到一定程度后,肩胛骨处在发力的有利位置后可以主动推。 用腰部的力量 在上一个练习基础上, 抱水时候,体会抱水侧腰部伸展。 滚动,退肩的同时,同侧腰部也有意收紧。 以上,通过腰部伸展抱到更多的水,通过腰部带动背阔肌发力推水。 2022 PB 今天泳池人很少,游出了2022年的PB。

2022年2月20日 · 1 分钟 · 费小慢

Leo editor在python下的开发即将结束

Leo 要停止开发了 早上在邮件列表里面读到EKR的一封信: Leo 6.6 may be the last substantial release in Leo’s history. At present, the 6.6 to-do list contains five items. There are no items at present on the 6.7 to-do list. Many open items remain, but I have little desire to do any of them. Expect 6.6 final in a month or so. The leojs project now seems like the future of Leo. Indeed, it melds Leo with vs code, an unbeatable combination imo. ...

2022年2月19日 · 1 分钟 · 费小慢

JavaScript的异步执行机制

回顾: JavaScript语言定义的 callback, promise, await, asyc JavaScript/NodeJS 引擎(libuv) 操作系统IO多路复用(epoll). 了解 JavaScript 异步函数的运行和实现机制,方便阅读和调试JavaScript代码。 1. 简介 对于习惯了线程池/多进程/消息队列的程序员,使用JavaScript/NodeJS,有几个概念需要了解。 Callbacks, Promises, Async Await IO多路复用和异步访问 2. Callbacks, Promises, Async Await Youtube有个视频Async JS Crash Course - Callbacks, Promises, Async Await, 用24分钟把这几个概念演示的很清楚。 Callback是最基本的,但是Callback有一个问题,在写异步函数的时候,需要把callbank函数作为参数传进去。 如果这个函数本身是个异步函数,它也有一个callback函数作为参数。如果层数很多,就产生了callback hell。 函数可读性很差。 为了解决这个问题, Promise引入了一层封装, 在定义异步函数时候不需要指明callback函数了。 可以在Promise定义后再: 通过then来定义执行成功后的回调 通过catch定义执行异常情况下的回调 Async Await是对Promise的进一步封装。让异步函数看起来象同步函数(不再需要Promose里面的then来显式定义回调)。 下面抄自google官方文档 async function myFirstAsyncFunction() { try { const fulfilledValue = await promise; } catch (rejectedValue) { // … } } 如果在函数定义之前使用了 async 关键字,就可以在函数内使用 await。 当您 await 某个 Promise 时,函数暂停执行,直至该 Promise 产生结果,并且暂停并不会阻塞主线程。 如果 Promise 执行,则会返回值。 如果 Promise 拒绝,则会抛出拒绝的值。 ...

2022年2月12日 · 2 分钟 · 费小慢