我有这段代码:
function foo(input: Blob): void;
function foo(input: string): void;
function foo(input: any) {
if (typeof input === "string") {
}
else if (input instanceof Blob) {
}
}
在这里,我想在块范围内输入input
,而不是将其重新分配给另一个变量。
if (typeof input === "string") {
declare input: string;
}
else if (input instanceof Blob) {
declare input: Blob;
}
而不是:
if (typeof input === "string") {
var str: string = input;
}
else if (input instanceof Blob) {
var blob: Blob = input;
}
这在TypeScript中是否可行?
答案 0 :(得分:1)
不可能。您需要具有不同类型的新变量名称。
答案 1 :(得分:1)
我认为最新版本的TypeScript会自动执行此操作。但您必须与typeof
,instanceof
或特殊函数一起使用if语句。
function foo(input: string|blob|whatever) {
if(typeof input === 'string') {
// input is now a string
input.blobfunction(); // Error: blobfunction() is not a method of string
} else if(input instanceof blob) {
// input is now a blob
} else if(isWhatever(input)) {
// input is now a whatever
}
}
// Probably a good idea to make this a static 'whatever.isInstance()'
function isWhatever(input: string|blob|whatever): input is whatever {
return true; // put your test for whatever here
}
请参阅:https://www.typescriptlang.org/docs/handbook/advanced-types.html
答案 2 :(得分:0)
开始!新版本有增强的推理能力!
类似的东西
const a: string | string[];
if (Array.isArray(a)) {
a.map() // array here (automatically inferred)
} else {
a.trim() // string here (automatically inferred)
}
编译器可以毫无问题地推断出很多地方!
// typeof
if (typeof a === 'number') {
// number
}
// instance of
if (a instanceof SomeClass) {
// SomeClass
}
编译器在很多情况下都很聪明地进行推断!哪里很清楚!
问题!
丑陋的代码如:
if (PREDEFINED_STRATEGIES[(settings.strategy as keyof typeof PREDEFINED_STRATEGIES)]) {
StrategyClass = PREDEFINED_STRATEGIES[
settings.strategy as keyof typeof PREDEFINED_STRATEGIES
];
} else {
每次都添加 as keyof typeof PREDEFINED_STRATEGIES
进行转换并不酷!
没有在整个范围内投射一次的原生机制!
原生方式是在你施放它的地方使用另一个变量!
const strategy = settings.strategy as keyof typeof PREDEFINED_STRATEGIES;
if (PREDEFINED_STRATEGIES[strategy]) {
StrategyClass = PREDEFINED_STRATEGIES[strategy];
} else {
然而,这将创建一个不必要的变量!这将在运行时运行!
这是一个代码示例:
ts
interface IStrategy {
init(settings: any): Promise<this>,
getAmount(): number
}
export type StrategyClass = new () => IStrategy;
interface IPredefinedStrategies {
[name: string]: StrategyClass
}
const PREDEFINED: IPredefinedStrategies = {
'SomeStrategy': class SomeStrategy {} as StrategyClass
};
const strategy: string | StrategyClass = 'someStrategy';
// <<<<<<<<<<<<<<< casting with a variable >>>>>>>>>>>>>>>
const mStrategy = strategy as keyof typeof PREDEFINED;
let strategyClass: StrategyClass;
if (PREDEFINED[mStrategy]) {
strategyClass = PREDEFINED[mStrategy];
} else {
strategyClass = class SomeStrategy { } as StrategyClass;
}
const instance = new strategyClass();
js 编译输出:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const PREDEFINED = {
'SomeStrategy': class SomeStrategy {
}
};
const strategy = 'someStrategy';
const mStrategy = strategy; // <<<<<< you can see the variable here ending in final code >>>>>>
let strategyClass;
if (PREDEFINED[mStrategy]) {
strategyClass = PREDEFINED[mStrategy];
}
else {
strategyClass = class SomeStrategy {
};
}
const instance = new strategyClass();
//# sourceMappingURL=index.tscVersion.js.map
您可能会问的问题? Typescript 是否优化了最终代码! (任何配置选项...)=> 核心上没有!
您可以查看here
<块引用>非目标
如果你想要这样的东西!
可以想到三件事! 变形金刚或宏!或者 js 优化器!
对于 js 优化器,我可以提到 Google Closure 编译器!在高级水平高度优化代码! 保持最小化和处理和删除死代码!类似于 C++ 编译器优化!
例如不需要的(额外变量)将被自动删除!
可以让 ts 构建先行!然后在build文件夹上执行Closure编译器! (npm run build
脚本可以这样)
如果为浏览器构建东西! 闭包编译器的使用可以产生很大的不同!无论是大小还是优化!就像缩小的用法一样!在浏览器中 js 包大小!并且还解析编译所有事情!代码越小!更好!加上删除任何不必要的东西和 js 代码!你可以看看这个很棒的reference and document
简而言之,如果您已经在使用 Closure compiler
。 使用局部变量进行转义只是最简单的好方法!我们可以依靠 Closure 编译器为我们移除它!
您可以检查Closure compiler here
知道有cli工具(jar文件(java中构建))!
我已经在我的计算机中设置了它(命令别名为 closureCompiler
和 cc
)
在Linux中:
.bashrc(.zshrc,如果您使用的是 .zshrc)
# Closure compiler
export CLOSURE_COMPILER_FILE=$HOME/opt/closureCompiler/closure-compiler.jar
closureCompiler() {
java -jar $CLOSURE_COMPILER_FILE $@
}
cc() {
closureCompiler $@
}
您可以在网络应用(playground)上查看该工具
您可以在下面的示例中查看优化是如何进行的:
我们可以看到无意义的变量是如何被删除的!
从我们的第一个例子中获取代码:
我们可以看到高级是如何优化的!
要知道 Closure Compiler 仍在不断更新!有很多选择!您可以通过打印 cli 命令帮助 (--help) 来检查!支持优化输出到不同的es目标! ...
ECMASCRIPT3, ECMASCRIPT5,
ECMASCRIPT5_STRICT, ECMASCRIPT_2015,
ECMASCRIPT_2016, ECMASCRIPT_2017,
ECMASCRIPT_2018, ECMASCRIPT_2019,
ECMASCRIPT_2020, STABLE,
ECMASCRIPT_NEXT (latest features)
对于宏! Ts 内核不支持
https://github.com/microsoft/TypeScript/issues/4892
但是有一些工具可以添加如下内容:
https://github.com/blainehansen/macro-ts
(这个包是作者的实验!不是全部成熟!然后我很容易看到不必要的增加的复杂性!还有配置文件......!我没看太远!我不会判断包!)
>我建议的是通过 ts 转换器的宏功能。
想到宏!就像你写的那样!那么它就会被移除!
让我先分享变压器资源! (与宏无关!他们是一个很好的话题)
https://blog.logrocket.com/using-typescript-transforms-to-enrich-runtime-code-3fd2863221ed/
https://github.com/madou/typescript-transformer-handbook
https://levelup.gitconnected.com/writing-typescript-custom-ast-transformer-part-1-7585d6916819
https://blogs.u2u.be/diedrik/post/typescript-transformers-transform-and-rise-up
<块引用>不幸的是,现在 tsconfig.json 不允许指定自定义 AST 转换器。
您可以使用多种替代方法,每种方法都有自己的警告:
===
范围投射功能!本来可以通过直接变压器实现的! (没时间试这个)!
我准备了一个 repo(测试书)!详细说明方法!
我们将使用 ttypescript 包来使用带有转换器的打字稿!
<块引用>Typescript 不通过 tsconfig 支持转换器!只能通过编码api!
使用 ttypescript!我们将能够通过 tsconfig.json 添加转换器。
我们将使用包装上的变压器typescript-transform-macros
这个转换器提供hygienic macro。
在继续之前!如果你不知道它们是什么,你可以检查一些 ts 转换器的参考:
我尝试了所有这些!你可以在下面的 Repo 中查看它!有详细的解释!了解变压器的链接!代码示例!
https://github.com/MohamedLamineAllal/TypescriptMacroTestBook
宏示例:
declare function MACRO<T>(t: T): T;
interface IStrategy {
init(settings: any): Promise<this>,
getAmount(): number
}
export type StrategyClass = new () => IStrategy;
interface IPredefinedStrategies {
[name: string]: StrategyClass
}
const PREDEFINED: IPredefinedStrategies = {
'SomeStrategy': class SomeStrategy {} as StrategyClass
};
const strategy: string | StrategyClass = 'someStrategy';
// <<<<<<<<<<<<<<<< Defining macro >>>>>>>>>>>>>>>>
const mStrategy = MACRO(
strategy as keyof typeof PREDEFINED
);
let strategyClass: StrategyClass;
if (PREDEFINED[mStrategy]) {
strategyClass = PREDEFINED[mStrategy];
} else {
strategyClass = class SomeStrategy { } as StrategyClass;
}
const instance = new strategyClass();
输出:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const PREDEFINED = {
'SomeStrategy': class SomeStrategy {
}
};
const strategy = 'someStrategy'; // <<<<<< no macro >>>>>>
let strategyClass;
if (PREDEFINED[strategy]) {
strategyClass = PREDEFINED[strategy];
}
else {
strategyClass = class SomeStrategy {
};
}
const instance = new strategyClass();
//# sourceMappingURL=index.js.map
你可以看到它是如何工作的!这是完美的基于作用域的投射方式!
对于宏转换器,您也可以查看 babel 宏插件链接:
https://github.com/codemix/babel-plugin-macros
很酷的东西! ttypescript 很直观!感觉只是带有变形金刚支持的简单打字稿!一样的命令!只需添加一个额外的 t!所以没有矫枉过正!
变压器易于设置!
tsconfig
{
compilerOptions: {
// ....
"plugins": [
{ "transform": "typescript-transform-macros" }
]
}
}
当然还有安装软件包:
npm install typescript ttypescript typescript-transform-macros --save-dev
所以是的,设置太快了!哪里过分了!不是真的!
但关键是在大多数情况下添加一个无用的变量不会影响性能!
另一方面!使用宏可能太有趣了!包括一些好的优化!就像拥有数组迭代的宏一样!并有流畅的语法!然后将其转换为正常的for循环!因为它的性能更好!这在高迭代应用程序中很重要!和处理!
示例:
declare function MACRO<T>(t: T): T;
const MAP = MACRO(
<T, L>(
inputConst: T[],
visitor: (value: T, index?: number, input?: T[]) => L
) => {
const input = inputConst;
const length = input.length;
const result = new Array(length) as L[];
for (let i = 0; i < length; i++) {
result[i] = visitor(input[i], i, input);
}
return result;
}
);
declare interface Array<T> {
MAP: Array<T>["map"];
}
console.log([1, 2, 3].MAP(n => 3 * n + 1));
输出:
"use strict";
const input_1 = [1, 2, 3];
const length_1 = input_1.length;
const result_1 = new Array(length_1);
for (let i_1 = 0; i_1 < length_1; i_1++) {
result_1[i_1] = 3 * input_1[i_1] + 1;
}
console.log(result_1);
//> [ 4, 7, 10 ]
这样的东西可以派上用场!所以喜欢的人可以试一试!
否则只需使用普通变量就可以了!使用闭包编译器之类的优化器也可以删除它!出于多种原因,这是一个不错的选择!特别是如果代码是针对浏览器的!
否则宏可能太有趣了!对于很多事情!
我更重视可能性!然后说!在大多数情况下,这无关紧要!创建一个变量!或者只是使用丑陋的铸造!对于这种无用的事情,您不需要了解很多元素!否则变压器和宏的设置太简单了!所以使用优化器!值得检查和了解!
那些是我能看到的选项!那很容易搭配!并且直观!