TypeScript是包含类型的ES6 Javascript的超集。可以使用class
关键字声明一个类,并使用new
关键字进行实例化,类似于它们在Java中的关键字。
我想知道TypeScript中是否有任何用例可以在不使用new
关键字的情况下实例化类。
我问的原因是因为我想知道是否假设我有一个名为Bob
的类,我是否可以假设Bob
的任何实例都使用new Bob()
进行实例化。
答案 0 :(得分:7)
Typescript保护措施,所以如果你这样做:
class A {}
let a = A();
你会收到错误:
类型
typeof A
的值不可调用。你的意思是包括在内吗? '新的'?
但是,有些对象可以在不使用new
关键字的情况下创建,基本上都是原生类型
如果查看lib.d.ts,您可以看到不同构造函数的签名,例如:
interface StringConstructor {
new (value?: any): String;
(value?: any): string;
...
}
interface ArrayConstructor {
new (arrayLength?: number): any[];
new <T>(arrayLength: number): T[];
new <T>(...items: T[]): T[];
(arrayLength?: number): any[];
<T>(arrayLength: number): T[];
<T>(...items: T[]): T[];
...
}
正如您所看到的那样,有new
个关键字的情况总是相同的
如果你愿意,你当然可以模仿这种行为。
重要的是要理解的是,虽然打字稿检查以确保不会发生这种情况,但javascript不会检查,因此如果有人编写将使用您的代码的js代码,他可能会忘记使用new
,所以这种情况仍有可能。
很容易检测到这是否在运行时发生,然后按照您认为合适的方式处理它(抛出错误,通过使用new
返回实例来修复它并记录它。)
这是一篇关于它的帖子:Creating instances without new(普通的js),但是tl; dr是:
class A {
constructor() {
if (!(this instanceof A)) {
// throw new Error("A was instantiated without using the 'new' keyword");
// console.log("A was instantiated without using the 'new' keyword");
return new A();
}
}
}
let a1 = new A(); // A {}
let a2 = (A as any)(); // A {}
据我所知,不可能让编译器理解A
可以在没有new
关键字的情况下调用any
而不进行投射。
我们可以做得更好,而不是把它投到interface AConstructor {
new(): A;
(): A;
}
let a2 = (A as AConstructor)(); // A {}
:
Array
我们无法为lib.d.ts
中的interface Array<T> {
...
}
interface ArrayConstructor {
...
}
declare const Array: ArrayConstructor;
执行诀窍的原因:
Array
在这里他们使用df = pd.DataFrame({'count': [False] * 12, 'index': [0, 1] * 6, 'cols': ['a', 'b', 'c'] * 4})
print(df)
一次作为一个类型而一次作为一个值,但是一个类既是一个类型又是一个值,所以试图做这个技巧将结束:
重复标识符'A'
答案 1 :(得分:0)
这是非常棘手的,但是可以通过多种方式完成,具体取决于您希望其与行为类似的程度。像这样的内置Array
构造函数。
这不是TypeScript特有的,这是JavaScript的问题。
在JavaScript中,如果没有class
,则不能调用用new
关键字创建的构造函数:
> class A {}
undefined
> new A()
A {}
> A()
TypeError: Class constructor A cannot be invoked without 'new'
就像箭头功能无法用new
调用一样:
> B = () => {}
[Function: B]
> B()
undefined
> new B()
TypeError: B is not a constructor
只有在使用function
和不使用new
的情况下,才能调用使用> function C() {}
undefined
> C()
undefined
> new C()
C {}
关键字创建的函数:
class
(有趣的是,如果将箭头功能和A()
关键字构造函数都转换为ES6之前的JS,则上面的所有B()
,C()
和new
都可以使用不论是否使用function
,因为它们都将使用new
关键字转换为旧样式函数,即使在当前引擎上也可以正常工作。)
一旦克服了调用构造函数的错误问题,就需要确保构造函数实际上获得了一个新对象。
在JavaScript(和TypeScript)中,this
关键字创建一个新对象并将其绑定到构造函数中的function f() {
console.log(this);
}
,然后将其返回。因此,如果您有一个功能:
new f()
然后,如果您将其称为f()
,它将打印并清空对象并将其返回。
如果您将其称为new
而没有window
,则它将打印全局对象(在浏览器中显示{global in Node or
或in web worker - see my module on npm [the-global-object](https://www.npmjs.com/package/the-global-object) for more info) and it will return
自身any
未定义`。 / p>
此问题是TypeScript特定的。您需要确保所有类型都能按预期工作并且以有用的方式工作。将所有内容声明为class
很容易,但是随后您将失去编辑器中的所有提示,并且TypeScript编译器在编译期间将不会检测到类型错误。
此问题再次不是特定于TypeScript而是针对JavaScript。
您希望一切都能按预期工作-使用旧函数和显式原型进行继承,并使用extends
和class
关键字进行继承以及更多工作。
换句话说,该对象应与用new
声明并用Array
实例化且没有任何花哨内容的其他对象相同。
我的经验法则:如果您可以使用内置new
之类的东西(无论是否使用A()
都可以),那么您也应该使用我们的构造函数。
与JavaScript通用。
您想要的不仅是获得一个在没有new
的情况下调用x instanceof A
的对象,而且实际上您希望console.log()
能够按预期工作,您还希望{{1} },以便在您要打印对象等时写下正确的名称。
这可能不是每个人的问题,但需要考虑。
function
它应该支持class
语法,而不是返回到function
构造函数和原型,否则您将失去很多有用的TypeScript功能。
这与上面的问题#6 有关-如果转换目标是ES6之前的版本,则结果将使用旧式function
构造函数,但不会产生错误:< / p>
TypeError: Class constructor A cannot be invoked without 'new'
(请参阅上面的问题1 )
这可能对您来说不是问题。如果您仍在为旧式引擎进行转译,则不会出现此问题,但是当您更改转译目标时(例如,避免async
/ await
polyfills等的高运行时间成本),您会代码会中断。如果这是一个库代码,那么它将不适用于所有人。如果只供您自己使用,则至少要记住它。
这是一段时间前我想到的一些解决方案。我对他们不是100%满意,我想避免使用代理,但是这些是我发现可以解决上述所有问题的唯一解决方案。
我的第一次尝试(有关更多常规类型,请参见后面的示例):
type cA = () => A;
function nonew<X extends Function>(c: X): AI {
return (new Proxy(c, {
apply: (t, _, a) => new (<any>t)(...a)
}) as any as AI);
}
interface A {
x: number;
a(): number;
}
const A = nonew(
class A implements A {
x: number;
constructor() {
this.x = 0;
}
a() {
return this.x += 1;
}
}
);
interface AI {
new (): A;
(): A;
}
const B = nonew(
class B extends A {
a() {
return this.x += 2;
}
}
);
这样做的一个缺点是,虽然构造函数名称正确并且可以正常打印,但是constructor
属性本身指向原始构造函数,该原始构造函数是nonew()
函数的参数,而不是函数返回(取决于您如何掠夺它,这可能会或可能不会是一个问题)。
另一个缺点是需要声明接口以公开类型。
另一种解决方案:
type NC<X> = { new (): X };
type FC<X> = { (): X };
type MC<X> = NC<X> & FC<X>;
function nn<X>(C: NC<X>): MC<X> {
return new Proxy(C, {
apply: (t, _, a) => new (<any>t)(...a)
}) as MC<X>;
}
class $A {
x: number;
constructor() {
this.x = 0;
}
a() {
return this.x += 1;
}
}
type A = $A;
const A: MC<A> = nn($A);
Object.defineProperty(A, 'name', { value: 'A' });
class $B extends $A {
a() {
return this.x += 2;
}
}
type B = $B;
const B: MC<B> = nn($B);
Object.defineProperty(B, 'name', { value: 'B' });
这里,您不需要在冗余接口中重复类型定义,而是可以使用带有$
前缀的原始构造函数。
在这里,您还将获得继承和instanceof
的工作,并且构造函数的名称和打印都可以,但是constructor
属性指向带有$
前缀的构造函数。
另一种方法:
type NC<X> = { new (): X };
type FC<X> = { (): X };
type MC<X> = NC<X> & FC<X>;
function nn<X>(C: NC<X>): MC<X> {
return new Proxy(C, {
apply: (t, _, a) => new (<any>t)(...a)
}) as MC<X>;
}
type $c = { $c: Function };
class $A {
static $c = A;
x: number;
constructor() {
this.x = 10;
Object.defineProperty(this, 'constructor', { value: (this.constructor as any as $c).$c || this.constructor });
}
a() {
return this.x += 1;
}
}
type A = $A;
var A: MC<A> = nn($A);
$A.$c = A;
Object.defineProperty(A, 'name', { value: 'A' });
class $B extends $A {
static $c = B;
a() {
return this.x += 2;
}
}
type B = $B;
var B: MC<B> = nn($B);
$B.$c = B;
Object.defineProperty(B, 'name', { value: 'B' });
此解决方案的实例的constructor
属性指向暴露的实例(不是$
前缀的构造函数),但是使{{1}的constructor
属性返回true
}}-但是hasOwnProperty()
是false
的地方,所以这应该不是问题。
我将所有尝试和更多解释放在GitHub上:
我对他们中的任何一个都不完全满意,但是他们都按照自己的方式工作。
的回答