TypeScript 前端工程最佳实践

作者:王春雨

前言

随着前端工程化的快速发展, TypeScript 变得越来越受欢迎,它已经成为前端开发人员必备技能。 TypeScript 最初是由微软开发并开源的一种编程语言,自2012年10月发布首个公开版本以来,它已得到了人们的广泛认可。TypeScript 发展至今,已经成为很多大型项目的标配,其提供的静态类型系统,大大增强了代码的可读性、可维护性和代码质量。同时,它提供最新的JavaScript特性,能让我们构建更加健壮的组件,新版本不断迭代更新,编写前端代码也越来越香。

typescript 下载量变化趋势(来自于 npm trends)

1 为什么使用 TypeScript

微软提出 TypeScript 主要是为了实现两个目标:为 JavaScript 提供可选的类型系统,兼容当前及未来的 JavaScript 特性。首先类型系统能够提高代码的质量和可维护性,国内外大型团队经过不断实践后得出一些结论:

  • 类型有利于代码的重构,它有利于编译器在编译时而不是运行时发现错误;
  • 类型是出色的文档形式之一,良好的函数声明胜过冗长的代码注释,通过声明即可知道具体的实现;

像其他语言都有类型的存在,如果强加于 JavaScript 之上,类型可能会有一些不必要的复杂性,而 TypeScript 在两者之间做了折中处理尽可能地降低了入门门槛,它使 JavaScript 即 TypeScript ,为 JavaScript 提供了编译时的类型安全。TypeScript 类型完全是可选的,原来的 .js 文件可以直接被重命名为 .ts ,ts 文件可以被编译成标准的 JavaScript 代码,并保证编译后的代码全部兼容,它也被成为 JavaScript 的 “超集”。没有类型的 JavaScript 语法虽然简单灵活,使用的变量是弱类型,但是比较难以掌握,TypeScript 提供的静态类型检查,很好的弥补了 JavaScript 的不足。

TypeScript 类型可以是隐式的也可以是显式的,它会尽可能安全地推断类型,以便在代码开发过程中以极小的成本为你提供类型安全,也可以使用显式的声明类型注解让编译器编译出我们想要的内容,更重要的是为下一个必须阅读代码的开发人员理解代码逻辑。

类型错误也不会阻止JavaScript 的正常运行,为了方便把 JavaScript 代码迁移到 TypeScript,即使存在编译错误,TypeScript 也会被编译出完整的 JavaScript 代码,这与其他语言的编译器工作方式有很大不同,这也正是 TypeScript 被青睐的另一个原因。

TypeScript 的特点还有很多比如下面这些:

  1. 免费开源,使用 Apache 授权协议;
  2. 基于ECMAScript 标准进行拓展,是 JavaScript 的超集;
  3. 添加了可选静态类型、类和模块;
  4. 可以编译为可读的、符合ECMAScript 规范的 JavaScript;
  5. 成为一款跨平台的工具,支持所有的浏览器、主机和操作系统;
  6. 保证可以与 JavaScript 代码一起使用,无须修改(这一点保证了 JavaScript 项目可以向 TypeScript 平滑迁移);
  7. 文件扩展名是 ts/tsx;
  8. 编译时检查,不污染运行时;

总的来说我们没有理由不使用 TypeScript, 因为 JavaScript 就是 TypeScript,TypeScript 可以让 JavaScript 更美好。

2 开始使用 TypeScript

2.1 安装 TypeScript 依赖环境

TypeScript 开发环境搭建非常简单,大部分前端工程都集成了 TypeScript 只需安装依赖增加配置即可。所有前端项目都离不开 NodeJS 和 npm 工具,npm 命令安装 TypeScript,通常TypeScript 自带的 tsc 并不能直接运行TypeScript 代码,因此我们还会安装 TypeScript 的运行时 ts-node:

npm install --save-dev typescript ts-node

2.1.1 集成 Babel

前端工程大都离不开 Babel ,我们需要将 TypScript 和 Babel 结合使用,TypeScript 编译器负责对代码进行静态类型检查,Babel 负责将TypeScript 代码转译为可以执行的 JavaScript 代码:

Babel 与 TypeScript 结合的关键依赖 @babel/preset-typescript,它提供了从 TypeScript 代码中移除类型相关代码(如,类型注解,接口,类型文件等),并在 babel.config.js 文件添加配置选项:

npm install -D @babel/preset-typescript

// babel.config.js
{
"presets": [
// ...
"@babel/preset-typescript"
]
}

2.1.2 集成 ESlint

代码检查是项目的重要组成部分,TypeScript 自身的约束相对简单只可以发现一些代码错误并不会帮助我们统一代码风格,当项目越来越庞大,开发人员越来越多时,代码风格的约束还是必不可少的。我们可以借助 ESLint对代码风格进行约束,为了让 eslint 来解析 TypeScript 代码我们需要安装解析器 @typescript-eslint/parser 和 插件 @typescript-eslint/eslint-plugin:

npm install --save-dev @typescript-eslint/parser @typescript-eslint/eslint-plugin

注意: @typescript-eslint/parser 和 @typescript-eslint/eslint-plugin 必须使用相同的版本
在 .eslintrc.js 配置文件中添加选项:

"parser": "@typescript-eslint/parser",
      "plugins": ["@typescript-eslint"],


// 可以直接启用推荐的规则
  "extends": [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended"
]
// 也可以选择自定义规则
"rules": {
"@typescript-eslint/no-use-before-define": "error",
// ...
}

自定义规则选项具体解读:

2.2 配置 TypeScript

TypeScript 本身提供了只使用参数在命令行编译 TypeScript 文件,但是在实际项目开发时我们都会使用 tsconfig.json ,如果项目中没有此文件,可以手动创建也可以使用命令行创建(tsc —init)。使用 TypeScript 初期仅需要一份默认的 tsconfig.json 即可,它包含了一下基本的编译选项相关信息,当我们需要定制编译选项时就需要去了解每一项具体的含义,编译选项解读如下:


2.严格的类型检查选项:

  • strict: 是否启用严格类型检查选项,可选 ture | false
  • allowUnreachableCode: 是否允许不可达的代码出现,可选 ture | false
  • allowUnusedLabels: 是否报告未使用的标签错误,可选 ture | false
  • noImplicitAny: 当在表达式和声明上有隐式的 any 时是否报错,可选 ture | false
  • strictNullChecks: 是否启用严格的 null 检查,可选 ture | false
  • noImplicitThis: 当 this 表达式的值为 any 时,生成一个错误,可选 ture | false
  • alwaysStrict: 是否以严格模式检查每个模块,并在每个文件里加入 use strict,可选 ture | false
  • noImplicitReturns: 当函数有的分支没有返回值时是否会报错,可选 ture | false
  • noFallthroughCasesInSwitch: 表示是否报告 switch 语句的 case 分支落空(fallthrough)错误;

3.模块解析选项:

  • moduleResolution: 模块解析策略默认为 node 比较通用的一种方式基
  • commonjs 模块标准,另一种是 classic 适用于其他 module 标准,如 amd、 umd、 esnext 等等
  • baseUrl: “./“ 用于解析非相对模块名称的根目录
  • paths: 模块名到基于 baseUrl 的路径映射的列表,格式 {}
  • rootDirs: 根文件夹列表,其做好内容表示项目运行时的结果内容,格式 []
  • typeRoots: 包含类型声明的文件列表,格式 [“./types”] ,相对于配置文件的路径解析;
  • allowSyntheticDefaultImports: 是否允许从没有设置默认导出的模块中默认导入

4.Source Map 选项:

  • sourceRoot: ./ 指定调试器应该找到 TypeScript 文件而不是源文件的位置
  • mapRoot: ./ 指定调试器应该找到映射文件而不是生成文件的位置
  • inlineSourceMap: 是否生成单个 sourceMap 文件,不是将 sourceMap 生成不同的文件
  • inlineSources: 是否将代码与 sourceMap 生成到一个文件中,要求同时设置 inlineSourceMap 和 sourceMap 属性

5.其它选项:

  • experimentalDecorators: 是否启用装饰器
  • emitDecoratorMetadata: 是否为装饰器提供元数据的支持

6.还可以使用include 和 exclude 选项来指定编译器需要和不需要编译的文件,一般增加必要的 exclude 文件会提升编译性能:

"exclude": [
    "node_modules",
    "dist"
...
  ],

2.3 TypeScript 类型注解

熟悉了 TypeScript 的相关配置,再来看一看 TypeScript 提供的基本类型,下图是与 ES6 类型的对比:


图中蓝色的为基本类型,红色为 TypeScript 支持的特殊类型

TypeScript 的类型注解相当于其它语言的类型声明,可以使用 let 和 const 声明一个变量,语法如下:

// let 或 const 变量名:数据类型 = 初始值;
//例如:
let varName: string = 'hello typescript'

函数声明,推荐使用函数表达式,也可以使用箭头函数显得更简洁一下:

let 或 const 函数表达式名 = function(参数1:类型,参数2:类型):类型{
// 执行代码
// return xx;
}
// 例如
let sum = function(num1: number, num2: number): number {
return num1 + num2;
}

2.4 TypeScript 特殊类型介绍

typescript 基本类型的用法和其它后端语言类似在这里不进行详细介绍,TypeScript 还提供了一些其它语言没有的特殊类型在使用过程中有很多需要注意的地方。

2.4.1 any 任意值

any 在 TypeScript 类型系统中占有特殊的地位。它为我们提供了一个类型系统的“后门”,TypeScript 会把类型检查关闭,它能够兼容所有的类型,因此所有类型都能被赋值给它。但我们必须减少对它的依赖,因为需要确保类型安全,除非必须使用它才能解决问题,当使用 any 时,基本上是在告诉 TypeScript 编译器不用进行任何类型检查。
任意值类型和 Object 有相似的作用,但是 Object 类型的变量只允许给它赋值不同类型的值,但是却不能在它上面调用方法,即便真有这些方法:

2.4.2 void、null 和 undefined

空值(void)、null 和 undefined 这几个值类似,在使用的过程中很容易混淆,以下依次进行说明:

  • 空值 void 表示不返回任何值,一般用于函数定义返回类型时使用,用 void 关键字表示没有任何返回值的函数,void 类型的变量只能赋值为 null 和 undefined,不能赋值给其他类型上(除了 any 类型以外);
  • null 表示不存在的对象值,一般只当作值来用,而不是当作类型使用;
  • undefined 表示变量已经声明但是尚未初始化的变量的值,undefined 通常也是当作值来使用;
    null 和 undefined 是所有类型的子类型,我们可以把 null 和 undefined 赋值给任何类型的变量。如果开启了 strictNullChecks 配置,那么 null 和 undefined 只能赋值给 void 和它们自身,这能避免很多常见的问题。

2.4.3 枚举

TypeScript 语言支持枚举类型,它是对JavaScript 标准数据类型的一个补充。枚举取值被限定在一定范围内的场景,在实际开发中有很多场景都适合用枚举来表示,枚举类型可以为一组数据赋予更加友好的名称,从而提升代码的可读性,使用 enum 关键字来定义:

enum SendType {
SEND_NORMAL,
SEND_BATCH,
SEND_FRESH,
...
}
console.log(SendType.SEND_NORMAL === 0) // true
console.log(SendType.SEND_BATCH === 1) // true
console.log(SendType.SEND_FRESH === 2) // true

一般枚举的声明都采用首字母大写或者全部大写的方式,默认枚举值是从 0 开始编号。也可以手动编号为数值型或者字符串类型:

// 数值枚举
enum SendType {
SEND_NORMAL = 1,
SEND_BATCH = 2,
SEND_FRESH,  // 按以上规则自动赋值为 3
...
}
const sendtypeVal =  SendType.SEND_BATCH; 

// 编译后输出代码
var SendType;
(function (SendType) {
    SendType[SendType["SEND_NORMAL"] = 1] = "SEND_NORMAL";
    SendType[SendType["SEND_BATCH"] = 2] = "SEND_BATCH";
    SendType[SendType["SEND_FRESH"] = 3] = "SEND_FRESH"; // 按以上规则自动赋值为 3
})(SendType || (SendType = {}));

var sendtypeVal =  SendType.SEND_BATCH; 

// 字符串枚举
enum PRODUCT_CODE {
  P1 = 'ed-m-0001', // 特惠送
  P2 = 'ed-m-0002', // 特快送
  P4 = 'ed-m-0003', // 同城即日
  P5 = 'ed-m-0006', // 特瞬送城际
}

这样写法编译后的常量代码比较冗长,而且在运行时 sendtypeVal 的取值不变,将会查找变量 SendType 和 SendType.SEND_BATCH。我们还有一个可以使代码更简洁且能获得性能提升的小技巧那就是使用常量枚举(const enum)。

// 使用常量枚举编译前
const enum SendType {
    SEND_NORMAL = 1,
    SEND_BATCH = 2,
    SEND_FRESH  // 按以上规则自动赋值为 3
}

const sendtypeVal =  SendType.SEND_BATCH;

// 编译后
var sendtypeVal = 2 /* SendType.SEND_BATCH */;

2.4.4 never 类型

大多数情况我们并不需要手动定义 never 类型,只有在写一些非常复杂的类型和类型工具方法,或者为一个库定义类型等情况下才需要用到它,never 类型一般出现在函数抛出异常或存在无法正常结束的情况下。

2.4.5 元组类型

元组类型的声明和数组比较类似,只是元组中的各个元素类型可以不同。简单示例如下:

// 元祖示例
let row: [number, string, number] = [1, 'hello', 88];

2.4.6 接口 interface

接口是 TypeScript 的一个核心概念,它能将多个类型声明组合成一个类型注解:

interface CountDown {
readonly uuid: string // 只读属性
  time: number
  autoStart: boolean
  format: string
value: string | number // 联合类型,支持字符串和数值型
[key: string]: number // 字符串的键,数值型的值
}
interface CountDown {
  finish?: () => void // 可选类型
  millisecond?: boolean // 可选方法
}
// 接口可以重复声明,多次声明可以合并为一个接口

接口可以继承其它类型对象,相当于将继承的对象类型复制到当前接口:

interface Style {
color: string
}
interface: Shape {
name: string
}
interface: Circle extends Style, Shape {
radius: number

// 还会包含继承的属性
// color: string
// name: string
}
const circle: Circle = { // 包含 3 个属性
radius: 1,
color: 'red',
name: 'circle'
}

如果子接口与父接口之间存在同名的类型成员,那么子接口中的类型成员具有更高优先级。

2.4.7 类型别名 type

TypeScript 提供了为类型注解设置别名的便捷方法——类型别名,类型别名就是可以给一个类型起一个新名字。在 TypeScript 中使用关键字 type 来描述类型变量:

type StrOrNum = string | number
// 用法和其它基本类型一样
let sample: StrOrNum
sample = 123
sample = '123'
sample = true // 错误

与接口区别,我们可以为任意类型注解设置别名,这在联合类型和交叉类型中比较实用,下面是一些常用方法

type Text = string | { text: string } // 联合类型

type Coordinates = [number, number] // 元组类型
type Callback = (data: string) => void // 函数类型

type Shape = { name: string } // 对象类型
type Circle = Shape & { radius: number} // 交叉类型,包含了 name 和 radius 属性

如果需要使用类型注解的层次结构,请使用接口,它能使用implements 和 extends。为一个简单的对象类型使用类型别名,只需要给它一个语义化的名字即可。另外,想给联合类型和交叉类型提供一个语义化的别名时,使用类型别名更加合适而不是用接口。类型别名与接口的区别如下:

  1. 类型别名能够表示非对象类型,接口则只能表示对象类型,因此我们想要表示原始类型、联合类型和交叉类型时只能使用类型别名;
  2. 类型别名不支持继承,接口可以继承其它接口、类等对象类型,类型别名可以借助交叉类型来实现继承的效果;
  3. 接口名总是会显示在编译器的诊断信息和代码编辑器的智能提示信息中,而类型别名的名字只在特定情况下显示;
  4. 接口具有声明合并的行为,而类型别名不会进行声明合并;

2.4.8 命名空间 namespace

随着项目越来越复杂,我们需要一种手段来组织代码,以便于在记录它们类型的同时还不用担心与其它对象产生命名冲突。因此我们把一些代码放到一个命名空间内,而不是把它们放到全局命名空间下。现实生活中,一个学校里经常会出现同名同姓的同学,如果在不同班里,就可以用班级名+姓名来区分。其实命名空间与班级名的作用一样,可以防止同名的函数和变量相互影响。
TypeScript 中命名空间使用 namespace 关键字来定义,基本语法格式:

namespace 命名空间名 {
const 私有变量; 
export interface 接口名;
export class 类名;
}
// 如果需要在命名空间外部调用需要添加 export 关键字
命名空间名.接口名;
命名空间名.类名;
命名空间名.私有变量; // 错误,私有变量不允许访问

在构建比较复杂的应用时,往往需要将代码分离到不同的文件中,以便进行维护,同一个命名空间可以出现在多个文件中。尽管是不同的文件,但是它们依然是同一个命名空间,使用时就如同它们在一个文件中定义的一样。

// 多文件命名空间
// Validation.ts
namespace Validation {
export interface StringValidator {
isAcceptable(s: string): boolean;
}
}

// NumberValidator.ts
namespace Validation { // 相同命名空间
export interface NumberValidator {
isAcceptable(num: number): boolean;
}
}

2.4.9 泛型

TypeScript 设计泛型的关键动机是在成员之间提供有意义的类型约束,这些成员可以是类的实例成员、类的方法、函数的参数、函数的返回值。使用泛型,可以将相同的代码用于不同的类型(语法:一般在类名、方法名的后面加上<泛型> ),一个队列的简单实现与泛型的示例:

class Queue {
private data = []
push = item => this.data.push(item)
pop = () => this.data.shift()
}

const queue = new Queue()
// 在没有约束的情况下,开发人员很可能进入误区,导致运行时错误(或潜在问题)
queue.push(0) // 最初是数值类型
queue.push('1') // 有人添加了字符串类型

// 使用过程中,走入了误区
console.log(queue.pop().toPrecision(1));
console.log(queue.pop().toPrecision(1)); // 运行时错误

一个解决办法可以解决以上问题:

class QueueOfNumber {
private data: number[] = []
push = (item: number) => this.data.push(item)
pop = (): number => this.data.shift()
}
const queue = new Queue()

queue.push(0) 
queue.push('1') // 错误,不能放入一个 字符串类型 的数据

这么做如果需要一个字符串的队列,怎么办?需要重写一遍类似的代码?这时就可以用到泛型,可以让放入的类型和取出的类型一样:

class Queue<T> {
private data: T[] = []
push = (item: T) => this.data.push(item)
pop = (): T | undefined => this.data.shift()
}
// 数值类型
const queue = new Queue<number>()
queue.push(0) 
queue.push(1) 
// 或者 字符串类型
const queue = new Queue<string>()
queue.push('0')
queue.push('1')

我们可以随意指定泛型的参数类型,一般使用简单的泛型时,常用 T、U、V 表示。如果在我们的参数里,拥有不止一个泛型,就应该使用更加语义化的名称,如 TKey 和 TValue。依照惯例,以 T 作为泛型的前缀,在其它语言已经是约定俗成的方式了。

2.4.10 类型断言

TypeScript 程序中的每一个表达式都具有某种类型,编译器可以通过类型注解或类型推导来确定表达式类型,但有时,开发者比编译器更清楚某个表达式的类型,因此就需要用到类型断言,类型断言(Type Assertion) 可以用来手动指定一个值的类型,告诉编译器应该是什么类型,具体语法如下:

  • expr(<目标类型>值、对象或者表达式);
  • expr as T (值或者对象 as 类型);
  • expr as const 或 expr 可以将某类型强制转换成不可变类型;
  • expr!(!类型断言):非空类型断言运算符 “!” 是 TypeScript 特有的类型运算符;
type AddressVO = { address: string }
(<AddressVO>sendAddress).address // <T> 类型断言
(sendAddress as AddressVO).address // as 类型断言

let val = true as const // 等于 const val = true
function getParams(router: { params: Array<string> } | undefined) {
if(!router) return ''

return router!.params // 告诉编译器 router 是非空的
}

3 深入 TypeScript 泛型编程

泛型编程是一种编程风格或者编程范式,它允许在程序中定义形式类型参数,然后在泛型实例化时使用实际类型参数来替换形式类型参数。刚开始进行 TypeScript 开发时,我们很容易重复的编写代码,通过泛型,我们能够定义更加通用的数据结构和类型。许多编程语言都很流行面向对象编程,可以创建公共接口的类并隐藏实现细节,让类之间进行交互,可以有效管理复杂度对复杂领域分而治之。但是对于前端来说泛型编程可以更好的解耦、组件化和可复用。接下来使用泛型处理一种常见的需求:通过示例创建独立的、可重用的组件。

3.1 解耦关注点

我们需要一个 getNumbers 函数返回一个数字数组,允许在返回数组之前对每一项数字应用一个变换处理函数,该函数接收一个数字返回一个新数字。如果调用者不需要任何处理,可以将只返回其结果的函数作为默认值。

type TransformFunction = (value: number) => number

function doNothing(value: number): number ( // doNothing() 只返回原数据,不进行任何处理
  return value
)

function getNumbers(transform: TransformFunction = doNothing): number[] {
    /** */
}

又出现另一种业务场景,有一个 Widget 对象数组,可以从 WidgetWidget 对象创建一个 AssembledWidget 对象。assembleWidgets() 函数处理一个 Widget 对象数组,并返回一个 AssembledWidget 对象数组。因为我们不想做不必要的封装,所以 assembleWidgets() 将一个 pluck() 函数作为实参,给定一个 Widget 对象数组时,pluck() 返回该数组的一个子集。允许调用者告诉函数需要哪些字段,从而忽略其余字段。

type PluckFunction = (widgets: Widget) => Widget[]

function pluckAll(widgets:  Widget[]):  Widget[] ( 
// pluckAll() 返回全部,不进行任何处理
  return widgets
)

// 如果用户没有提供 pluck() 函数,则返回 pluckAll 作为实参的默认值
function assembleWidgets(pluck: PluckFunction = pluckAll): AssembledWidget[] {
    /** */
}

仔细观察可以两处代码都有相似之处,doNothing() 和 pluckAll() 它们都接收一个参数,并不做处理就返回。它们的区别只是接收和返回的值类型不同:doNothing 使用数字,pluckAll 使用 Widget 对象数字,两个函数都是恒等函数。在代数中恒等函数指的是 f(x) = x。在实际开发中这种恒等函数会有很多,出现在各处,我们需要编写一个可重用的恒等函数来简化代码,使用 any 类型是不安全的它会绕过正常的类型检查,这时我们就可以使用泛型恒等函数:

function identity<T>(value: T):  T ( // 有一个类型参数 T 的泛型恒等函数
  return value
)
// 可以使用 identity 代替 doNothing 和 pluckAll

采用这种实现方式,可以将恒等逻辑与实际业务逻辑问题进行更好的解耦,恒等逻辑可以完全独立出来。这个恒等函数的类型参数是 T,当为 T 指定了实际类型时,就创建了具体的函数。

泛型类型:是指参数化一个或多个类型的泛型函数、类、接口等。泛型类型允许我们编写能够支持不同类型的通用代码,从而实现高度的代码重用。使用泛型让代码的组件化程度更高,我们可以把这些泛型组件用作基本模块,通过组合它们实现期望的行为,同时在组件之间只保留下最小限度的依赖。

3.2 泛型数据结构

假如我们要实现一个数值二叉树和字符串链表。把二叉树实现为一个或多个结点,每个结点存储一个数值,并引用其左侧和右侧的子结点,这些引用指向结点,如果没有子结点,可以指向 undefined。

class NumberBinaryTreeNode {
  value: number
  left: NumberBinaryTreeNode | undefined
  right: NumberBinaryTreeNode | undefined

  constructor(value: number) {
    this.value = value
  }
}

类似地,我们实现链表为一个或多个结点,每个结点存储一个 string 和对下一个结点的引用,如果没有下一个结点,引用就指向 undefined。

class StringLinkedListNode {
  value: string
  next: StringLinkedListNode | undefined

  constructor(value: string) {
    this.value = value
  }
}

如果工程的其它部分需要一个字符串二叉树或者数值列表我们可以简单的复制代码,然后替换几个地方,复制从来不是一个好选择,如果原来的代码有Bug,很可能会忘记在复制的版本中修复 Bug。我们可以使用泛型来避免复制代码。
我们可以实现一个泛型的 NumberTreeNode,使其可用于任何类型:

class BinaryTreeNode<T> {
  value: T
  left: BinaryTreeNode<T> | undefined
  right: BinaryTreeNode<T> | undefined

  constructor(value: T) {
    this.value = value
  }
}

实际我们不应该等待有字符串二叉树的新需求才创建泛型二叉树:原始的 NumberBinaryTreeNode 实现在二叉树数据结构和类型 number 之间产生了不必要的耦合。同样,我们也可以把字符串链表替换成泛型的 LinkedListNode:

class LinkedListNode<T> {
  value: string
  next: LinkedListNode | undefined

  constructor(value: string) {
    this.value = value
  }
}

我们要知道,有很成熟的库已经提供了所需的大部分数据结构(如列表、队列、栈、集合、字典等)。介绍实现,只是为了更好的理解泛型,在真实项目中最好不要自己编写代码,可以从库中选择泛型数据结构,去阅读库中泛型数据结构的代码更有助于提升我们的编码能力。一个可以迭代的泛型链表完整实现供参考如下:

type IteratorResult<T> = {
  done: boolean
  value: T
}

interface Iterator<T> {
  next(): IteratorResult<T>
}

interface IterableIterator<T> extends Iterator<T> {
    [Symbol.iterator](): IterableIterator<T>;
}

function* linkedListIterator<T>(head: LinkedListNode): IterableIterator<T> {
  let current: LinkedListNode<T> | undefined = head
  while (current) {
    yield current.value // 在遍历链表过程中,交出每个值
    current = current.next
  }
}

class LinkedListNode<T> implements Iterable<T> {
  value: T
  next: LinkedListNode<T> | undefined

  constructor(value: T) {
    this.value = value
  }

  // Symbol.iterator 是 TypeScript 特有语法,预示着当前对象可以使用 for ... of 遍历
  [Symbol.iterator](): Iterator<T> { 
    return linkedListIterator(this)
  }
}

我们使用了生成器在遍历数据结构的过程中会交出值,所以使用它能够简化遍历代码。生成器返回一个 IterableIterator,所以我们可以直接在 for … of 循环中使用。
以上对泛型编程的介绍只是凤毛菱角,其实泛型编程支持极为强大的抽象和代码可重用性,使用正确的抽象时,我们可以写出简洁、高性能、容易阅读且优雅的代码。

4 TypeScript 注释指令

4.1 常用注释指令

TypeScript 编译器可以通过编译选项设置对所有 .ts 和 .tsx 文件进行类型检查。但是在实际开发中有些代码可能无法避免检查错误,因此 TypeScript 提供了一些注释指令来忽略或者检查某个JavaScript 文件或者代码片段:

  • // @ts-nocheck: 为某个文件添加这个注释,就相当于告诉编译器不对该文件进行类型检查。即使存在错误,编译器也不会报错;
  • // @ts-check: 与上个注释相反,可以在某个特定的文件添加这个注释指令,告诉编译器对该文件进行类型检查;
  • // @ts-ignore: 注释指令的作用是忽略对某一行代码进行类型检查,编译器进行类型检查时会跳过指令相邻的下一行代码;4.2 JSDoc 与类型JSDoc 是一款知名的为 JavaScript 代码添加文档注释的工具,JSDoc 利用 JavaScript 语言中的多行注释结合特殊的“JSDoc 标签”来为代码添加丰富的描述信息。
    TypeScript 编译器可以自动推断出大部分代码的类型信息,也能从 JSDoc 中提取类型信息,以下是TypeScript 编译器支持的部分 JSDoc 标签:
  • @typedef 标签能够创建自定义类型;
  • @type 标签能够定义变量类型;
  • @param 标签用于定义函数参数类型;
  • @return 和 @returns 标签作用相同,都用于定义函数返回值类型;
  • @extends 标签定义继承的基类;
  • @public @protected @private 标签分别定义类的公共成员、受保护成员和私有成员;
  • @readonly 标签定义只读成员;

4.3 三斜线指令

三斜线指令是一系列指令的统称,它是从 TypeScript 早期版本就开始支持的编译指令。目前,已经不推荐继续使用三斜线指令了,因为可以使用模块来取代它的大部分功能。简单了解一下即可,它以三条斜线开始,并包含一个XML标签,有几种不同的语法:

5 TypeScript 内置工具类型

TypeScript 提供了很多内置的工具类型根据不同的应用场景选择合适的工具可以减轻很多工作,减少冗余代码提升代码质量,下面列举了一些常用的工具:

  • Partial:构造一个新类型,并将类型 T 的所有属性变为可选属性;
  • Required:构造一个新类型,并将类型 T 的所有属性变为必选属性;
  • Readonly: 构造一个新类型,并将类型 T 的所有属性变为只读属性;
  • Pick: 已有对象类型中选取给定的属性名,返回一个新的对象类型;
  • Omit: 从已有对象类型中剔除给定的属性名,返回一个新的对象类型;
    示例代码:
interface A {
  x: number
  y: number
  z?: string
}
type T0 = Partial<A>
// 等价于 
type T0 = {
    x?: number | undefined;
    y?: number | undefined;
    z?: string | undefined;
}

type T1 = Required<A>
// 等价于
type T1 = {
    x: number;
    y: number;
    z: string;
}

type T2 = Readonly<A>
// 等价于
type T2 = {
    readonly x: number;
    readonly y: number;
    readonly z?: string | undefined;
}

type T3 = Pick<A, 'x'>
// 等价于
type T3 = {
    x: number;
}

type T4 = Omit<A, 'x'>
// 等价于
type T4 = {
    y: number;
    z?: string | undefined;
}

6 TypeScript 提效工具

6.1 TypeScript 演练场

TypeScript 开发团队提供了一款非常实用的在线代码编辑工具——TypeScript 演练场
地址:https://www.typescriptlang.org/zh/play

  • 左侧编写 TS 代码,右侧自动生成编译后的代码;
  • 可以自主选择 TypeScript 编译版本;
  • 版本列表最后一项是一个特殊版本 “Nightly” 即 “每日构建版本”,想尝试最新功能可以试试;
  • 支持 TypeScript 大部分配置项和编译选项,可以模拟本地环境,查看代码片段的输出结果;

6.2 JSDoc Generator 插件

如果使用的是 vscode 编辑器直接搜索( JSDoc Generator 插件)插件地址:https://marketplace.visualstudio.com/items?itemName=crystal-spider.jsdoc-generator 安装成功后,使用 Ctrl + Shift + P 打开命令面板,可以进行如下操作可以自动生成带有 TypeScript 声明类型的文档注释:

  • 选择 Generate JSDoc 为当前光标处代码生成文档注释;
  • 选择Generate JSDoc for the current file 为当前文件生成文档注释;

    6.3 代码格式化工具
    VSCode 仅提供了基本的格式化功能,如果需要定制更加详细的格式化规则可以安装专用的插件来实现。我们使用 Prettier 功能非常强大(推荐使用),它是目前最流行的格式化工具: https://prettier.io/,同时也提供了一个在线编辑器:https://prettier.io/playground/6.4 模块导入自动归类和排序在多人协作开发时代码越来越复杂,一个文件需要导入很多模块,每个人都会加加着加着就有点乱了,绝对路径的、相对路径的,自定义模块、公用模块顺序和类别都是混乱的,模块导入过多还会出现重复的。引入 TypeScript 之后检查更加严格,导入的不规范会有错误提示,如果只靠手动优化工作量大且容易出错。VSCode 编辑器提供了按字母顺序自动排序和归类导入语句的功能,直接按下快捷键“Shift + Alt + O”即可优化。也可以通过右键菜单“Source Action” 下的 “Organize Imports” 选项来进行优化导入语句。6.5 启用 CodeLens

CodeLens 是一项特别好用的功能,它能够在代码的位置显示一些可操作项,例如:

  • 显示函数、类、方法和接口等被引用的次数以及被哪些代码引用;
  • 显示接口被实现的次数以及谁实现了该接口;

VSCode 已经内置了 CodeLens 功能,只需要在设置面板开启,找到TypeScript 对应的 Code Lens 两个相关选项并勾选上:


开启后的效果,出现引用次数,点击 references 位置可以查看哪里引用了:

6.6 接口自动生成 TypeScript 类型

对于前端业务开发来说,最频繁的工作之一就是和接口打交道,前端和接口之间经常出现出入参不一致的情况,后端的接口定义也需要在前端定义相同的类型,大量的类型定义如果都靠手写不仅工作量大而且容易出错。因此,我们需要能够自动生成这些接口类型定义的 TypeScript 代码。VSCode 插件市场就有这样一款插件——Paste JSON as Code 。
插件地址:https://marketplace.visualstudio.com/items?itemName=quicktype.quicktype
安装这个 VSCode 插件可以将接口返回的数据,自动转换成类型定义接口文件。
1.剪贴板转换成类型定义:首先将 JSON 串复制到剪贴板, Ctrl + Shift + P 找到命令:Paste JSON to Types -> 输入接口名称

{"a":1,"b":"2","c":3} // 复制这段 JSON 代码

// Generated by https://quicktype.io
export interface Obj {
  a: number;
  b: string;
  c: number;
}

2.JSON 文件转换类型定义(这个更常用一些):打开 JSON 文件使用Ctrl + Shift + P 找到命令: Open quicktype for JSON。下图为 package.json 文件生成类型定义的示例:


对应大量且冗长的接口字段一键生成是不是很方便呢!希望这些工具能给每一位研发带来帮助提升研发效率。

7 总结

TypeScript 是一个比较复杂的类型系统,本文只是对其基本用法进行了简要说明和工作中用到的知识点,适合刚开始使用 TypeScript 或者准备使用的研发人员,对于更深层次的架构设计和技术原理并未提及,如果感兴趣的可以线下交流。用好 TypeScript 可以编写出更好、更安全的代码希望对读到本文的有所帮助并能在实际工作中运用。希望本文作为 TypeScript 入门级为读者做一个良好的开端。感谢阅读!!

相关推荐

AI 会写代码之后,交付为什么还没变快?

AI 写代码,只是这场变革的起点,而不是终点。真正决定一个组织能否吃到 AI 红利的,不是"能不能用 AI 写出代码",而是敢不敢用 AI 重新设计交付流程、重新划分角色分工、重新组织团队结构。研发视角能把工具和个人推到极致,却会在组织的边界前止步;只有站上组织视角,把AI × 工具一路推进到AI × 组织,那些被 AI 照出来的摩擦,才会真正被抹平。国际技术部 Multi AI Native 想做的,正是这件事——让 Agent 成为你可靠的数字战友,让每一次迭代交付都快一个量级;

京东科技开发者 246

OxygenData-Vibe搭建:用 AI Coding重新定义数据看板搭建

长期以来,数据看板开发主要有两种方式:一种是使用标准化 BI 工具,交付快、成本低,但图表样式、交互体验和功能扩展都受限于 BI 本身的能力;另一种是自研定制开发,能够充分满足复杂和个性化需求,但需要投入大量研发资源,交付周期也更长。比如一个通过 BI 只需要 1 天就能搭建出来的看板,如果采用自研方式,可能需要投入 30 PD 的研发人力。既然 BI 交付这么快,为什么不把所有看板需求都交给 BI?原因在于,业务某些场景需要的不只是把数据展示出来。

京东科技开发者 309

当我谈数据库高可用时我谈些什么

大家都经常听到说数据库的双1,那双1指的是什么呢?参数=1(最安全)≠1(更快但可能丢)每次提交都把 redo 刷盘0/2:攒着刷,崩溃丢最近 1 秒每次提交都把 binlog 刷盘0/N:攒着刷,崩溃丢最近若干条"双 1"(都设为 1)= 单机不丢已提交事务的前提。生产核心库必须双 1;代价是每次提交两次 fsync。小结:单机层用 WAL 提速、用 2PC 保证 redo/binlog 一致、用崩溃恢复裁决半成品事务、用双 1 兜底持久性。

京东科技开发者 271

ScoAI:面向供应链的运筹原生决策智能

ScoAI 关注的并非 AI 能否进入供应链,而是 AI 参与供应链决策之后,哪些内容适合通过数据学习,哪些关系必须显式计算,以及哪些结果需要依据执行记录进行验证。其基本分工是:供应链业务定义问题、资源与行动后果,运筹学表达目标、约束和协同关系,AI 提供可复用的估计与判断。真正用于改进后续决策的,不仅是模型生成的初始结果,还包括实际采取的行动及其产生的业务反馈。

京东科技开发者 367

拆解海博 AI-Native 落地保障:海博团队 AI 知识库能力建设

绕了一大圈,回头看,我们其实一直在解开头那三个结:AI 读不懂系统、影响面靠人脑记、测试预期没处查。做到这里,本质上是把"AI 用不好"这个问题,从"是不是该换个更强的模型"重新定义成了"怎么把上下文经营好"。顺着这个思路,我们攒下了这么几样东西:•。

京东科技开发者 518

从零构建一个生产级记忆型 AI Agent —— AgentScope 项目全景技术与学习指南

domain 层端口接口(domain)职责适配器实现(infra)对话流水账读写经验记忆检索/落库请求级幂等四态会话元数据 CRUD会话级 Skill 选择态Skill 元数据HITL 权限网关(domain 内含逻辑,见第四篇)StreamSink技术中立的流式下沉(client 层桥接)以幂等端口为例,头部注释明确写道:实现 domain 端口,持有实体不出 infra 边界,对外只暴露嵌套视图GateResult。

京东科技开发者 571

数据涅槃:Agentic 重塑搜推数据生产体系

当前大模型已经能够理解需求、生成代码和解释错误,但数据生产的难点并不只是“写出一段 SQL”。一个需求还要经过数据源确认、口径澄清、加工编排、资源规划、任务提交、调度运行、结果交付和故障处置。任何一步缺少都可能让“看起来正确”的任务变成生产事故。

京东科技开发者 337

81.8 秒的视频,我们改了 15 个版本:一次纯 Codex 驱动的 AI-native 视频实践

81.8 秒、15 个版本,这些数字真正想说明什么?视频制作正在从一组专业软件里的复杂操作,变成一场围绕目标、参考和反馈展开的持续对话。AI 已经可以接住从脚本到交付的大量工作。你可以把精力留给更重要的部分:理解观众、选择故事、判断好坏。过去停在 PPT 和脑海里的想法,现在有机会被快速做成一支能看、能改、也愿意转发的视频。如果明天就要介绍你手头的项目,你最想先把哪一步交给 AI?

京东科技开发者 433

Codex 驱动的移动端AI全栈开发:从 Relay 原型图到可交付链路

AI 编程工具降低了跨端开发门槛,但“让 AI 写页面”并不等于“完成移动端交付”。PC 运营端页面通常以功能闭环为中心,设计约束较弱,研发可以基于组件库快速搭建;移动端 C 端页面直接面对用户,Relay 原型图和 UI 设计稿不仅是视觉参考,更是交互、信息层级、状态反馈和业务信任感的综合契约。

京东科技开发者 566

Claude Code vs Codex 记忆体系深度对比

Claude Code —— 分层自动记忆系统四层分层架构(CLAUDE.md / Auto Memory / Session Memory / AutoDream)构成自优化闭环,系统主动捕获、分类、去冗并跨会话接力经验。走的是自动化优先路线——让机器替人记忆、替人整理,以少量可控性的不确定换取强大的经验积累能力。Codex —— 声明式契约系统两层扁平架构(AGENTS.md 静态声明层 + 会话上下文层),记忆完全由人显式订立、显式修改,系统只读契约、绝不擅自变更。走的是可控性优先。

京东科技开发者 605

上游给空、下游拿到 -1,我把故障一直追到了 commons-beanutils 的构造函数

这篇手记是一份事后复盘,写在我们已经把修复推上线、把测试用例跑绿、把约束文档沉淀进团队库之后。非研发同事,可以直接从第八章开始看,前七章都是讲排障和代码分析。故障本身不复杂,修复也不复杂,但这次排查真正让我想写下来的,不是技术本身——而是从问题提报到解决全程AI的作用:问题定位:业务方反馈了一个异常单号说「下发失败」,一线运维同事直接在日志系统捞到相关的系统日志,用IDE打开下游代码库,把异常信息给到JoyCode——AI 直接指向了问题是serialType值非法,系统只接受0和1,而当前上游给的是-1。

京东科技开发者 703

SKill测评:谁才是真正的Skill之王?

巧合的是,阿里近期开源了一款名为 Skill-Up 的测评工具——它专门面向 Agent Skill 开发者,支持在 Claude Code、Codex、Qoder CLI 等真实 Agent Engine 环境中验证 Skill 的功能正确性,并可将失败用例转化为精准的修复指引,同时支持本地与 CI 环境的持续回归。2026年,Skill 的热度不减,已成为 AI 辅助编程领域的标配。在日常研发中,无论是沉淀个人经验,还是将团队的优秀范本标准化,将知识“蒸馏”为 Skill 已是大家的常规操作。

京东科技开发者 618

让AI真正参与工作!JoyDesk已上线京东云

JoyDesk提供覆盖AI资产管理、权限控制、安全策略、审批审计、运营监控等企业级治理能力,支持按组织、角色和项目进行精细化权限管理,并支持私有化部署,确保企业数据边界清晰、AI行为全程可追溯、安全策略灵活可配置,满足金融、政企等行业对数据安全和合规治理的要求,让AI真正成为企业可信赖的生产力。JoyDesk支持MCP协议、标准API、技能等多种开放接入方式,可快速连接企业现有业务系统,将业务能力封装为AI可调用的技能和专家,让AI能够理解业务流程、调用业务系统、完成业务任务,而无需改变企业原有IT架构。

京东科技开发者 652

【全栈实践】第一个 AI Agent 项目:从零搭建 AI 音频创作助手【最终版】

这个项目展示了AI Agent 在垂直内容创作领域端到端自动化:从内容理解到播客输出,全流程 AI 驱动多模态融合:文字、图片、音频、视频统一处理专业级音频质量:音色设计 + 智能混音,输出广播级品质低门槛使用:可视化配置 + 自然语言交互,无需技术背景。

京东科技开发者 556

【全栈实践】第一个 AI Agent 项目:从零搭建 AI 音频创作助手(高级篇)

这是一个 AI 驱动的播客创作助手,专注于从文字/视频/音频内容识别到播客制作的全流程。你可以用自然语言与它对话,也可以上传视频和音频,它会理解意图、调用相应的语音合成工具,把他们变成真实的播客音频。

京东科技开发者 720

【全栈实践】第一个 AI Agent 项目:从零搭建 AI 音频创作助手(进阶篇)

这是一个AI 驱动的音频内容创作助手。你可以用自然语言与它对话,它会理解意图、调用相应的语音合成工具,把文字变成真实的播客音频,并保存在本地。关于项目的详细介绍见第一篇文章:第一个 AI Agent 项目:从零搭建 AI 音频创作助手(入门篇)这篇文章更多介绍进阶篇添加的功能。

京东科技开发者 545

【全栈实践】第一个 AI Agent 项目:从零搭建 AI 音频创作助手(入门篇)

这个项目让我第一次把前端、后端、AI Agent 三块内容独立串联起来。技术选型上每一块都是当下主流的方案,实际跑通整条链路之后对"全栈"有了更具体的感受。框架版本要锁定:LangChain 这类快速迭代的框架,版本差异带来的问题远比你想象的多先跑通链路,再追求完美:新手阶段能把用户输入 → AI 处理 → 结果展示这条链跑通,比深入某个技术点更有价值多看官方文档:相比博客和教程,官方文档更新及时,特别是 LangGraph 这种新框架后续计划继续迭代,欢迎交流!

京东科技开发者 602

ICLR 2026 | 面向序列决策的贝叶斯集成方法

在这篇工作中,我们关注的是。

京东科技开发者 294

ICML 2026|NaviAgent:面向 Oxygen 智能体的可扩展工具编排

大型语言模型(LLM)越来越多地作为函数调用代理,调用外部工具来处理超出其静态知识范围的任务。然而,它们通常一次只调用一个工具,缺乏对任务结构的全局视图。由于工具之间往往相互依赖,这会导致错误累积和可扩展性差,尤其是在扩展到数百或数千个工具时。为了解决这些局限性,我们提出了NaviAgent,一种显式的双层架构,它通过基于图的工具关系建模将任务规划与工具执行解耦。在规划层,基于 LLM 的代理决定是直接响应、澄清意图,还是检索并执行一个独立于工具间复杂性的工具链。

京东科技开发者 383

实测 9 款 AI 架构图工具:从 Mermaid 美化到 GPT-Image2,一份选型清单

支持 5 类图(架构图、工作流图、时序图、数据流图、生命周期/状态机图),主题 4 种(Classic、Signal Flow、Blueprint、Editorial)+ 3 种色彩模式(Dark / Light / System Auto),输出格式含 HTML、PNG、SVG、WebM、Share Card PNG、JSON IR。它把提示词图库、图像生成案例、参考图编辑流程和命令行工具整合在一起,让 AI Agent 能直接调用 GPT Image 2 模型完成文生图、图生图、参考图编辑等任务。

京东科技开发者 895

别再守着 Claude Code 了——学会指挥它自主干活

回到开头那句:别再一句一句地喂它、然后守着屏幕。真正的用法是——把一件事想清楚、划好边界、给它一个能自我验证的目标,然后交出去。你会发现,省下来的时间不是一点半点。把重复的判断和执行还给机器,把真正需要你的判断留给自己。

京东科技开发者 584

CVPR 2026|Oxygen XR 团队在原生鱼眼高斯泼溅技术上带来新突破

京东零售技术团队联合清华大学提出DirectFisheye-GS框架,创新性地将鱼眼相机模型原生嵌入3D高斯泼溅管线,通过跨视角联合优化策略直接处理鱼眼图像,避免了传统去畸变预处理导致的信息损失。该方案在多个公开数据集上达到SOTA性能,有效解决了单视角优化引起的几何畸变和视角不一致问题,同时保持与现有3DGS工具链的兼容性。技术已应用于电商3D重建和4D体积视频等场景,显著提升了沉浸式体验。相关论文被CVPR 2026接收,为宽视场场景重建提供了高效解决方案。

京东科技开发者 353

拆解海博 AI-Native 落地保障:Harness、双 Loop、知识库与技能自主迭代实践

海博研发RoadMap 的落地,彻底终结了 AI 编程“对话式玄学”的旧模式,真正落地了AI Native 研发工程化。它证明 AI 规模化赋能研发的核心,不在于堆砌大模型能力,而在于用工程思维重构 AI 的工作范式:用结构化流程替代随机对话,用固化规则约束模型行为,用持续沉淀实现能力复利,用分层架构支撑规模化落地。

京东科技开发者 633

让 AI 快速「读懂」你的代码仓:Joy-Code-Graph 云端图谱服务的三次进化

代码知识图谱不是要取代 AI 的智能,而是要补齐它对代码全局关系的认知盲区。当 AI 能一眼看清「谁调用了谁、改动会波及哪里」,它写出的代码才真正靠谱;当这份认知安全地留在自己手里、随时被 Joygen 调用、并且永久沉淀在代码仓里——AI 编程的体验,才算真正上了一个台阶。这,就是我们做 Joy-Code-Graph 云端服务的初衷。能力共享,数据自主;一次建图,长期受用。

京东科技开发者 751

【Coding生态】从代码托管到 AI 能力底座:与Coding一起共建 AI 研发生态

前两种框架能够很好地支撑对话式能力和预设流程式能力。它们适合基于提示词或预设工作流完成研发问答、知识查询、流程编排等任务。但在更复杂的研发场景中,仅仅“回答问题”还不够。当用户业务逻辑复杂时,一旦场景进入真实落地阶段,例如下载代码、修改文件、执行命令、安装依赖、运行测试、读取完整代码文件,或者与自有系统联动、将结果渲染到 Coding 页面、推送到京 ME,AI 应用需要的不只是知识能力,还需要真实的代码库操作能力和独立可执行环境。

京东科技开发者 768

GPT-5.6、ChatGPT Work 与 Codex 更新食用指南

三档模型当前都提供 1,050,000 tokens 上下文、922,000 tokens 最大输入和 128,000 tokens 最大输出,接受文本与图像输入、输出文本,并支持 web search、file search、Computer Use、MCP、Tool Search、Skills、Apply Patch 等工具。Work 可以读取你提供的文件,通过插件连接 Slack、Google Drive、SharePoint、邮箱、日历、CRM 或项目系统,调用获批工具,并在执行中展示进度。

京东科技开发者 860

把业务流程沉淀成高质量 Skill 的实践路径

使用Record & Replay创建的Skill,尽管创建流程简单,但它的本质实际是让 AI 去操作页面,这不但有可能影响我们对电脑的操作,还有可能无法稳定复现。创建Skill时,可以使用 Codex 的 Record & Replay 插件,把一次手工操作录下来,再让 AI 生成可复用流程。刚开始切换时,很容易遇到接口报错,可以让Codex排查原因进行优化,直到可以稳定执行;补充相应业务背景,说明这件事为什么要做,谁会用,输入从哪来,输出给谁看。电脑休眠、关机、软件没开时,本地任务可能无法执行。

京东科技开发者 835

政企前台AI-Agent的架构实践:打造可复用的 AI 服务平台-【灯塔计划】

不止是爆火的龙虾,还是各种Skills,MCP、Agent,脱离的具体的场景谈方案,最后只会是一场空。这一套可复用的 AI 服务平台,截止目前,通过前后端对接规范文档,多项能力已快速接入到各个场域的不同端平台,大幅降低研发成本, 并且各业务线新场景的接入从原来的 “开发驱动” 转为 “配置驱动”,接入周期从天维度缩短至小时级。

京东科技开发者 860

京东 Oxygen xLLM 大模型推理引擎正式捐赠开放原子开源基金会,共建国产 AI Infra 生态

下一阶段 AI 基础设施的核心命题,不是简单的算力堆叠,而是让工程本身变得智能——让调度系统能自主感知负载特征并动态优化,让推理引擎能根据模型结构和硬件特性自动生成最优执行方案,让整个 AI 工程链路具备自感知、自决策、自优化的能力。未来,Oxygen xLLM 的项目官网、社区沟通渠道及微信社区将陆续向开发者开放,诚邀芯片厂商、模型厂商、云厂商、企业用户及个人开发者共同参与,让国产大模型推理引擎在开放生态中加速成长。硬件层,原生支持 GPU / NPU / MLU 多类 AI 芯片,深度适配国产芯片。

京东科技开发者 1000

【京东云】全球首个!京东全栈开源JoyAI-VL-Interaction,让大模型从“一问一答”走向“边看边说”

开源一套系统,而不只是一个模型在实时视频流中,JoyAI-VL-Interaction每秒都会做一次判断,比如,继续观察、保持沉默,发现关键事件、主动回应,遇到复杂任务,交给后台Agent处理。一个好的AI 助手,不应该一直打扰用户,而应该知道什么时候该出现,什么时候该安静,以及什么时候自己解决,什么时候交由agent解决。深耕零售、物流、健康、工业等实体产业二十余年,京东拥有全球领先的物理世界运营网络,覆盖仓储、配送、门店、直播、客服、售后海量真实场景,每天都在发生人、货、场的实时互动。

京东科技开发者 1029
上一篇: 从源码层面深度剖析Spring循环依赖
下一篇: MySql索引下推知识分享
京东云开发者
京东云开发者 企业官方账号 企业官方账号
博客等级 码龄8年 1万+粉丝 2139原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值