typescript 的 export export是什么意思思

问题对人有帮助,内容完整,我也想知道答案
问题没有实际价值,缺少关键内容,没有改进余地
接触typescript不久,现需要把以前的项目用ts重写一遍,遇到一个问题:
项目中db的orm都需要实例化才能使用,说明比较困难,请看原js代码:
//const Redis =
require('redis')
let initRedis = function(port, host){
return new Promise((success, fail) =& {
module.exports.redis = Redis.createClient(port, host);
success();
以下为我转换的ts代码:
const initRedis = function (port:number, host:string): Promise&void& {
return new Promise((success,fail)=&{
export let redis = Redis.createClient(port, host);
success();
遇到的错误:
error TS1184: Modifiers cannot appear here.
请问 如何才能正确的在执行initRedis方法后再导出redis?
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
可以参考这里
2.4是已经支持了,等下班回家给你写个范例
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
这个是做不到的。 Typescript的模块是标准符合 ES6 的模块标准, import 和 export 都是static的。
不过你可以使用类似下面的代码来做一些workaround。
// dynamic.ts
const _dynamic = {}
export function addDynamic() {
_dynamic['Redis'] = function () {
console.log('I am redis')
export const DYNAMIC = _dynamic
import { addDynamic, DYNAMIC } from '@/models'
addDynamic()
DYNAMIC['Redis']()
分享到微博?
你好!看起来你挺喜欢这个内容,但是你还没有注册帐号。 当你创建了帐号,我们能准确地追踪你关注的问题,在有新答案或内容的时候收到网页和邮件通知。还能直接向作者咨询更多细节。如果上面的内容有帮助,记得点赞 (????)? 表示感谢。
明天提醒我
关闭理由:
删除理由:
忽略理由:
推广(招聘、广告、SEO 等)方面的内容
与已有问题重复(请编辑该提问指向已有相同问题)
答非所问,不符合答题要求
宜作评论而非答案
带有人身攻击、辱骂、仇恨等违反条款的内容
无法获得确切结果的问题
非开发直接相关的问题
非技术提问的讨论型问题
其他原因(请补充说明)
我要该,理由是:请登录查看
TypeScript 声明文件结构概述一般来讲,你组织声明文件的方式取决于库是如何被使用的。 在JavaScript里提供了很多库的使用方法,这就需要你书写声明文件去匹配它们。 这篇指南涵盖了如何识别常见库的模式,与怎么样书写符合相应模式的声明文件。针对每种主要的库的组织模式,在一节都有对应的文件。 你可以利用它们帮助你快速上手。识别库的类型首先,我们先看一下TypeScript声明文件能够表示的库的类型。 这里会简单展示每种类型的库的使用方式,如何去书写,还有一些真实案例。识别库的类型是书写声明文件的第一步。 我们将会给出一些提示,关于怎样通过库的&使用方法及其源码来识别库的类型。 根据库的文档及组织结构不同,这两种方式可能一个会比另外的那个简单一些。 我们推荐你使用任意你喜欢的方式。全局库全局库是指能在全局命名空间下访问的(例如:不需要使用任何形式的import)。 许多库都是简单的暴露出一个或多个全局变量。 比如,如果你使用过&,$变量可以被够简单的引用:$(() =& { console.log('hello!'); } );
你经常会在全局库的指南文档上看到如何在HTML里用脚本标签引用库:&script src=&http://a.great.cdn.for/someLib.js&&&/script&
目前,大多数流行的全局访问型库实际上都以UMD库的形式进行书写(见后文)。 UMD库的文档很难与全局库文档两者之间难以区分。 在书写全局声明文件前,一定要确认一下库是否真的不是UMD。从代码上识别全局库全局库的代码通常都十分简单。 一个全局的“Hello, world”库可能是这样的:function createGreeting(s) {
return &Hello, & +
或这样:window.createGreeting = function(s) {
return &Hello, & +
当你查看全局库的源代码时,你通常会看到:顶级的var语句或function声明一个或多个赋值语句到window.someName假设DOM原始值像document或window是存在的你不会看到:检查是否使用或如何使用模块加载器,比如require或defineCommonJS/Node.js风格的导入如var fs = require(&fs&);define(...)调用文档里说明了如果require或导入这个库全局库的例子由于把一个全局库转变成UMD库是非常容易的,所以很少流行的库还再使用全局的风格。 然而,小型的且需要DOM(或&没有依赖)的库可能还是全局类型的。全局库模版模版文件定义了myLib库作为例子。 一定要阅读&。模块化库一些库只能工作在模块加载器的环境下。 比如,像&express只能在Node.js里工作所以必须使用CommonJS的require函数加载。ECMAScript 2015(也就是ES2015,ECMAScript 6或ES6),CommonJS和RequireJS具有相似的导入一个模块的表示方法。 例如,对于JavaScript CommonJS (Node.js),有下面的代码var fs = require(&fs&);
对于TypeScript或ES6,import关键字也具有相同的作用:import fs = require(&fs&);
你通常会在模块化库的文档里看到如下说明:var someLib = require('someLib');
或define(..., ['someLib'], function(someLib) {
与全局模块一样,你也可能会在UMD模块的文档里看到这些例子,因此要仔细查看源码和文档。从代码上识别模块化库模块库至少会包含下列具有代表性的条目之一:无条件的调用require或define像import * as a from 'b';&or&这样的声明赋值给exports或module.exports它们极少包含:对window或global的赋值模块化库的例子许多流行的Node.js库都是这种模块化的,例如,和&。UMDUMD模块是指那些既可以作为模块使用(通过导入)又可以作为全局(在没有模块加载器的环境里)使用的模块。 许多流行的库,比如&,就是这样的形式。 比如,在Node.js或RequireJS里,你可以这样写:import moment = require(&moment&);
console.log(moment.format());
然而在纯净的浏览器环境里你也可以这样写:console.log(moment.format());
识别UMD库会检查是否存在模块加载器环境。 这是非常形容观察到的模块,它们会像下面这样:(function (root, factory) {
if (typeof define === &function& && define.amd) {
define([&libName&], factory);
} else if (typeof module === &object& && module.exports) {
module.exports = factory(require(&libName&));
root.returnExports = factory(root.libName);
}(this, function (b) {
如果你在库的源码里看到了typeof define,typeof window,或typeof module这样的测试,尤其是在文件的顶端,那么它几乎就是一个UMD库。UMD库的文档里经常会包含通过require“在Node.js里使用”例子, 和“在浏览器里使用”的例子,展示如何使用&script&标签去加载脚本。UMD库的例子大多数流行的库现在都能够被当成UMD包。 比如&,,和许多其它的。模版针对模块有三种可用的模块,&,&&and&.使用,如果模块能够作为函数调用。var x = require(&foo&);
// Note: calling 'x' as a function
var y = x(42);
一定要阅读使用如果模块能够使用new来构造:var x = require(&bar&);
// Note: using 'new' operator on the imported variable
var y = new x(&hello&);
相同的作用于这些模块。如果模块不能被调用或构造,使用文件。模块插件或UMD插件一个模块插件可以改变一个模块的结构(UMD或模块)。 例如,在Moment.js里,&moment-range添加了新的range方法到monent对象。对于声明文件的目标,我们会写相同的代码不论被改变的模块是一个纯粹的模块还是UMD模块。模版使用模版。全局插件一个全局插件是全局代码,它们会改变全局对象的结构。 对于&全局修改的模块,在运行时存在冲突的可能。比如,一些库往Array.prototype或String.prototype里添加新的方法。识别全局插件全局通常很容易地从它们的文档识别出来。你会看到像下面这样的例子:var x = &hello, world&;
// Creates new methods on built-in types
console.log(x.startsWithHello());
var y = [1, 2, 3];
// Creates new methods on built-in types
console.log(y.reverseAndSort());
模版使用模版。全局修改的模块当一个全局修改的模块被导入的时候,它们会改变全局作用域里的值。 比如,存在一些库它们添加新的成员到String.prototype当导入它们的时候。 这种模式很危险,因为可能造成运行时的冲突, 但是我们仍然可以为它们书写声明文件。识别全局修改的模块全局修改的模块通常可以很容易地从它们的文档识别出来。 通常来讲,它们与全局插件相似,但是需要&require调用来激活它们的效果。你可能会看到像下面这样的文档:// 'require' call that doesn't use its return value
var unused = require(&magic-string-time&);
require(&magic-string-time&);
var x = &hello, world&;
// Creates new methods on built-in types
console.log(x.startsWithHello());
var y = [1, 2, 3];
// Creates new methods on built-in types
console.log(y.reverseAndSort());
模版使用模版。使用依赖可能会有以下几种依赖。依赖全局库如果你的库依赖于某个全局库,使用/// &reference types=&...& /&指令:/// &reference types=&someLib& /&
function getThing(): someLib.thing;
依赖模块如果你的库依赖于模块,使用import语句:import * as moment from &moment&;
function getThing(): moment;
依赖UMD库从全局库如果你的全局库依赖于某个UMD模块,使用/// &reference types指令:/// &reference types=&moment& /&
function getThing(): moment;
从一个模块或UMD库如果你的模块或UMD库依赖于一个UMD库,使用import语句:import * as someLib from 'someLib';
不要使用/// &reference指令去声明UMD库的依赖!补充说明防止命名冲突注意,在书写全局声明文件时,允许在全局作用域里定义很多类型。 我们十分不建义这样做,当一个工程里有许多声明文件时,它会导致无法处理的命名冲突。一个简单的规则是使用库定义的全局变量名来声明命名空间类型。 比如,库定义了一个全局的值&cats,你可以这样写declare namespace cats {
interface KittySettings { }
不要// at top-level
interface CatsKittySettings { }
这样也保证了库在转换成UMD的时候没有任何的破坏式改变,对于声明文件用户来说。ES6模块插件的影响一些插件添加或修改已存在的顶层模块的导出部分。 当然这在CommonJS和其它加载器里是允许的,ES模块被当作是不可改变的因此这种模式就不可行了。 因为TypeScript是能不预知加载器类型的,所以没没在编译时保证,但是开发者如果要转到ES6模块加载器上应该注意这一点。ES6模块调用签名的影响很多流行库,比如Express,暴露出自己作为可以调用的函数。 比如,典型的Express使用方法如下:import exp = require(&express&);
var app = exp();
在ES6模块加载器里,顶层的对象(这里以exp导入)只能具有属性; 顶层的模块对象&永远不能被调用。 十分常见的解决方法是定义一个&default导出到一个可调用的/可构造的对象; 一会模块加载器助手工具能够自己探测到这种情况并且使用&default导出来替换顶层对象。
意见反馈:
联系方式:
广告等垃圾信息
不友善内容
违反法律法规的内容
不宜公开讨论的政治内容使用TypeScript提高开发能力 - 简书
使用TypeScript提高开发能力
译者:张天军
本文为组织翻译,转载请注明出处。
世界在TypeScript的眼里是什么样子呢?你在使用TypeScript versus ES6编程的时候有什么得失呢?
如果你一直在琢磨这个问题,那么今天我们将深入地帮你列出答案。解决这个问题的最好办法是通过代码。所以让我们来深入了解它吧。在本文中,我们将改造 Kendo UI 的示例app中的其中一个 - Layout Digram App。我选择这个示例是因为它包含了各种排序的 Kendo UI 控制。由于我们很多人使用 Angular JS 开发,我们将继续并且从它的 jQuery 实现方式来重构(如果你不使用 Angular ,这个示例仍然是可以参考的,简单忽略特定的 Angular 位)。
(用安装);
一个支持的IDE。如下:
(我自己使用的IDE & 本文也是用这个 IDE)
(所有版本)
(TypeScript 定义管理器 - 命令行工具)
TSD(TYPESCRIPT DEFINITION MANAGER)你需要下载所有的。这个包含了我们项目中使用的JavaScript库的定义(AngularJS,lodash,KendoUI,等。)你可以把TSD命令行工具当做其它依赖包工具的等价物,比如Nuget,Bower,npm,等等。
安装TSD,你需要安装Node.js/npm:
npm install tsd -g
你现在可以搜索并且安装其它TypeScript包。TSD目前即包含JavaScript库的客户端定义,又包含JavaScript库的服务端定义。
tsd query angular
结果如下(简化):
- angular-agility
/ angular-agility
- angular-bootstrap-lightbox / angular-bootstrap-lightbox
- angular-dialog-service
/ angular-dialog-service
- angular-dynamic-locale
/ angular-dynamic-locale
- angular-file-upload
/ angular-file-upload
- angular-formly
/ angular-formly
- angular-gettext
/ angular-gettext
- angular-google-analytics
/ angular-google-analytics
- angular-growl-v2
/ angular-growl-v2
- angular-hotkeys
/ angular-hotkeys
- angularjs
然后你可以根据自己需求自定义安装:
tsd install angular --save
你可能会注意到TypeScript的定义文件已“d.ts”为后缀,比如 AngularJS 的 TypeScript定义文件 为 angular.d.ts 。在安装一个定义文件的时候忽略 --save 选项,如果文件不存在将创建一个tsd.d.ts文件,并且为我们项目中的每个TypeScript定义的依赖添加入口。
下面是运行命令行的目录结构图:
├── myapp/
├── typings
├── angularjs
├── angular.d.ts
├── tsd.d.ts
下面你会注意到,一条添加到Angular tsd依赖的命令是如何为我们 app/project 目录下的
提供其他的依赖的。
/// &reference path="express/express.d.ts" /&
/// &reference path="node/node.d.ts" /&
/// &reference path="stylus/stylus.d.ts" /&
/// &reference path="serve-favicon/serve-favicon.d.ts" /&
/// &reference path="morgan/morgan.d.ts" /&
/// &reference path="body-parser/body-parser.d.ts" /&
/// &reference path="errorhandler/errorhandler.d.ts" /&
/// &reference path="serve-static/serve-static.d.ts" /&
/// &reference path="mime/mime.d.ts" /&
/// &reference path="../public/lib/kendo-ui/typescript/kendo.all.d.ts" /&
/// &reference path="angularjs/angular.d.ts" /&
/// &reference path="angular-ui-router/angular-ui-router.d.ts" /&
/// &reference path="jquery/jquery.d.ts" /&
你可能会注意到列表中包含了Kendo UI tsd。有时我们下载的例如Kendo UI,angular-ui-router,和其他包括tsd文件的JavaScript库。在这些情况下,我们可以只打开tsd.d.ts文件,并且直接引用到我们项目app/project目录(使用相对路径)。
正如我前面所述,本文中,我们将使用AngularJS重构。这是一个很好的例子,因为它在案例app中使用了很多 Kendo UI组件,允许我们通过TypeScript和AngularJS使用大量的Kendo UI组件。
别名(可选)
在TypeScript中,有一个静态输入数据类型的概念,据我所知,大部分团队通常不管他们正在敲些什么,只是完全的限定。然而这篇文章中,我们将重命名大部分 Kendo UI,因而使他们根据简短。同样,你也可以跳过命名空间步骤的别名,并且按你所想的,对所有都完全限定。
例如,我们使用完全限定的命名空间初始化一个 ObservableArray :
var myArray = new kendo.data.ObservableArray([]);
那么,下面我们使用别名命名空间初始化一个 ObservableArray
import ObserverableArray = kendo.data.ObservableA // aliased
var myArray = new ObserverableArray([]); // initialized w/ alias
下一步,我们继续解决关注点分离。我们将从逻辑层和返回数据的view model分离view(presentation),例如组件初始化和数据绑定。
TypeScript接口抽象(可选)
作为一个最佳实践,我习惯为每一个 Angular Controller/ViewModel 创建一个接口,并且放在相同的文件中,作为 Controller的实现。为什么呢?这里有几个重要的原因:
ng Controller(class)的目的很明显,通过接口我们可以快速理解这个意图,用处和关注点。
理解什么是ng.IScope($scope)的界限。
下面是IDiagramController接口(diagram.controller.ts):
interface IDiagramController {
diagramWidget: D
diagramWidgetOptions: IDiagramO
canvasBackgroundColor:
selected: Array&any&;
selectedShape: S
selectedConnection: C
diagramZoomOptions: ISliderO
menuOptions: IMenuO
uploadOptions: IUploadO
splitterOptions: ISplitterO
panelBarOptions: IPointO
colorPickerOptions: IColorPickerO
canvasLayoutOptions: IDropDownListO
connectionCapOptions: IDropDownListO
windowWidgetOptions: IWindowO
shapeItemDraggableOptions: IDraggableO
alignConfigurationOptions: IButtonO
arrangeConfigurationOptions: IButtonO
windowWidget: K
shapePropertiesChange: (e: JQuery) =&
exportClick: (e: HTMLAnchorElement) =&
现在我们可以实现IDiagramController,我们尽量使用Controller/ViewModel,注意,下面,我们使用Angular注册DiagramController的地方实现这个类。我也建议使用Angular 1.x版本,因为无论何时升级到Angular v2,都会很好的兼容。
class DiagramController implements IDiagramController {
static $inject = ['$scope', '$window'];
constructor(private $scope: IDiagramScope, private $window: any) {
.module('diagram')
.controller('diagram.DiagramController', DiagramController);
TypeScript开发时间和完美编译时间
TypeScript 的好处是所有的类都是类型安全的,而且提供了一个支持确定开发时和编译时错误提醒。当完全由TypeScript编写时,当你的类型是混合类型或者使用静态语言例如C#,Java,C++,等等实现非静态操作时,你能够获取开发时和编译时的错误。
例如,如果你使用 Visual Studio Code,你好注意到,你能够获取接口没有被马上实现的警告,如果我们通过TypeScript获取编译错误。实际上我们使用混合类型是相同的(例如使用number声明,赋值string)。
Paste_Image.png
下面TypeScript能够从声明中推断出myArray是ObservableArray 的一种类型。然而我们设置myArray 为 ObservableObjectm TypeScript 会立即指示错误位置。
Paste_Image.png
重构操作和KENDO MENU
让我们来看看重构代码以支持新架构的案例,首先jQuery 和JavaScript版本:
var actions = {
blank: reset,
undo: undo,
redo: redo,
copy: copyItem,
paste: pasteItem
$("#menu ul").kendoMenu({
dataSource: [
{ text: "New", spriteCssClass: "new-item", items: [
{ text: "Blank", spriteCssClass: "blank-item", cssClass: "active" }
{ text: "Open&input id='upload' type='file' name='files' /&", encoded: false, spriteCssClass: "open-item", cssClass: "upload-item" },
{ text: "Save&a id='export' download='diagram.json'&&/a&", encoded: false, spriteCssClass: "save-item" },
{ text: "Undo", spriteCssClass: "undo-item", cssClass: "active" },
{ text: "Redo", spriteCssClass: "redo-item", cssClass: "active" },
{ text: "Copy", spriteCssClass: "copy-item", cssClass: "active" },
{ text: "Paste", spriteCssClass: "paste-item", cssClass: "active" }
select: function(e) {
var item = $(e.item),
itemText = item.children(".k-link").text();
if (!item.hasClass("active")) {
actions[itemText.charAt(0).toLowerCase() + itemText.slice(1)]();
与此相比,使用TypeScript和AngularJS 版本如下:
var actions: IMenuActions = {
blank: (e: IMenuSelectEvent): void =& {
this.diagramWidget.clear();
undo: (e: IMenuSelectEvent): void =& {
this.diagramWidget.undo();
redo: (e: IMenuSelectEvent): void =& {
this.diagramWidget.redo();
copy: (e: IMenuSelectEvent): void =& {
this.diagramWidget.copy();
paste: (e: IMenuSelectEvent): void =& {
this.diagramWidget.paste();
vm.menuOptions = {
dataSource: [
text: "New", spriteCssClass: "new-item", items: [
{ text: "Blank", spriteCssClass: "blank-item", cssClass: "active" }
{ text: "Open&input kendo-upload='upload' type='file' name='files' k-options='vm.uploadOptions' /&", encoded: false, spriteCssClass: "open-item", cssClass: "upload-item" },
{ text: "Save&a id='export' download='diagram.json' ng-click='vm.exportClick($event)'&&/a&", encoded: false, spriteCssClass: "save-item" },
{ text: "Undo", spriteCssClass: "undo-item", cssClass: "active" },
{ text: "Redo", spriteCssClass: "redo-item", cssClass: "active" },
{ text: "Copy", spriteCssClass: "copy-item", cssClass: "active" },
{ text: "Paste", spriteCssClass: "paste-item", cssClass: "active" }
select: (e: IMenuSelectEvent) =& {
var item = angular.element(e.item),
itemText = item.children(".k-link").text();
if (!item.hasClass("active")) {
actions[itemText.charAt(0).toLowerCase() + itemText.slice(1)](e);
重构SHAPEPROPERTIES变化事件
这里,我们将重构ShapeProperties变化事件,当其中一个属性(颜色,形状等)改变时,会同步更改选中对象的设计显示。
首先,jQuery和JavaScript版本:
$("#shapeProperties").on("change", shapePropertiesChange);
function shapePropertiesChange() {
var elements = selected || [],
options = {
fill: $("#shapeBackgroundColorPicker").getKendoColorPicker().value(),
color: $("#shapeStrokeColorPicker").getKendoColorPicker().value(),
width: $("#shapeStrokeWidth").getKendoNumericTextBox().value()
bounds = new Rect(
$("#shapePositionX").getKendoNumericTextBox().value(),
$("#shapePositionY").getKendoNumericTextBox().value(),
$("#shapeWidth").getKendoNumericTextBox().value(),
$("#shapeHeight").getKendoNumericTextBox().value()
for (i = 0; i & elements. i++) {
element = elements[i];
if (element instanceof Shape) {
element.redraw(options);
element.bounds(bounds);
下面让我来看看 TypeScript和AngularJS版本:
&span&Background Color:&/span&
&input kendo-color-picker
ng-model="vm.selectedShape.options.fill"
k-on-change="vm.shapePropertiesChange(kendoEvent)" /&
&span&Stroke Color:&/span&
kendo-color-picker
ng-model="vm.selectedShape.options.stroke.color"
k-on-change="vm.shapePropertiesChange(kendoEvent)" /&
&span&Stroke Width:&/span&
&input kendo-numeric-text-box type="text"
k-ng-model="vm.selectedShape.options.stroke.width"
k-on-change="vm.shapePropertiesChange(kendoEvent)" /&
&!-- code shortened for brevity--&
请看,你会发现借助于Angular的MVVM优势,我们再也不需使用jQuery selectors 去拼凑UI控件。现在我们直接绑定View到ViewModel:
public shapePropertiesChange = (e: JQuery): void =& {
var elements = this.selected || [];
var i: number,
elements.forEach((element) =& {
if (element instanceof Shape) {
var shape =
shape.redraw({
fill: this.selectedShape.options.fill,
stroke: this.selectedShape.options.stroke
shape.bounds(
this.selectedShape.height,
this.selectedShape.width,
this.selectedShape.x,
this.selectedShape.y
你可能会注意到,我们使用到了TypeScript中的新功能。例如,我们在上述forEach方法(aka fat arrows 和lambdas)中使用了箭头的功能。
随着最近发布的,我们如今甚至能够使用新功能开发,这个新功能可以兼容很多浏览器,即使不支持ES6或ES7。
INTELLISENSE
此外,即使是Kendo UI类型我们也可以获取 Intellisense,因为我们声明了selectedShape 并且定义为Kendo UI Shape 类型。
var selectedShape: kendo.dataviz.diagram.S
Paste_Image.png
显然,这将是和所有你导入的TSD库类型相似的,包含了jQuary,Angular 和 loadsh。
此外,我们现在可以实现一个真正的“查找所有依赖” 或者“查找所有使用”,举例来说,你能够为我们在ViewModel或者Angular控制器中的selectedShape实现一个“查找所有依赖”。如果这是用于工程间,我们也能够跨工程获取结果列表。
Paste_Image.png
如果你使用Visiual Studio Code,则能够打开“peek”视图,并且在右侧列出所有使用this.selectedShape的列表。你能够通过点击浏览每一个出现的地方,而且通过右侧的视图列表浏览也能够自动的滚动到出现的地方。
Paste_Image.png
其它存在TypeScript特性的有:
Solution-wide 重构
值得注意的是,这些特不仅Visual Studio Code独有的。在大多数支持TypeScript的IDE都可以使用。由于为JavaScript提供了强大的开发和编译时体验,因此TypeScrip带来了很多好处,可以提高你的开发效率。
我已经上传了文中介绍用TypeScript和AngularJS重构Kendo UI Diagram Application的sample到上。你可以访问。我也已经部署已完成和已经重构好的应用,。
在接下来的文章中,我打算使用Kendo替换TypeScript和Angular2 TypeScript with NativeScript。可以通过Twitter@lelong37 给我意见反馈或者直接留言。
祝编码愉快!
极客学院 WiKi 是集高质量内容,技术分享,作品出版,技术交流为一身的 IT 在线图文教育平台。
官方QQ群:
更多精彩IT课程请移步:}

我要回帖

更多关于 typescript的好处 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信