简体   繁体   English

如何在TypeScript中编写其成员是给定类的子类的类型

[英]How write a type whose members are subclasses of a given class in TypeScript

I would like to be able to say that a function only returns classes that extend some given class. 我想说一个函数只返回扩展某些给定类的类。

What I tried is to define a type AnimalClass 我试图定义一个AnimalClass类型

    interface AnimalClass {
        new( name : String ) : Animal ;
    }

I think this means that instances of AnimalClass are classes that have a constructor (or should I say "are a constructor") that takes a String and constructs an instance of Animal. 我认为这意味着AnimalClass的实例是具有构造函数(或应该说“是构造函数”)的类,该构造函数采用String并构造Animal的实例。

However the following code, which I think has an error, compiles with tsc version 1.7.5. 但是,以下代码(我认为有错误)是使用tsc版本1.7.5编译的。

interface AnimalClass {
    new( name : String ) : Animal ;
}

abstract class Animal {
    name : String ;
    constructor( name : String ) { this.name = name } 
    abstract kind() : String ;
}

class Lion extends Animal {
    kind() : String { return "lion" ; }
}

function getAnimalClass() : AnimalClass {
    return Lion ;  //  Shouldn't this be an error?
}

function makeAnimal() : Animal {
    const klass = getAnimalClass() ;
    return new klass( "bob" ) ;
}

var a = makeAnimal() ;
console.log( a.name + " is a " + a.kind() ) ;

I think it is in error because in function getAnimalClass I need that Lion is an instance of AnimalClass , but Lion does not have a constructor that takes a String . 我认为这是错误的,因为在函数getAnimalClass我需要LionAnimalClass的实例,但是Lion没有具有String的构造函数。

Perhaps Lion is inheriting the constructor from Animal . 也许Lion是从Animal继承构造函数的。 That would explain why there is no error and why the code runs and prints "bob is a lion" 那可以解释为什么没有错误,为什么代码运行并显示“ bob is a lion”

But no! 但不是! If I delete the constructor in Animal the code still compiles. 如果我删除Animal的构造函数,则代码仍会编译。 (Although now it prints "undefined is a lion"). (尽管现在它显示“ undefined is a lion”)。

So main question is: 所以主要问题是:

  • How do I write a type that describes objects which are classes that extend a given class and provide a constructor with a given parameter list? 如何编写一种描述对象的类型,这些对象是扩展给定类并为构造函数提供给定参数列表的类?

Secondary questions: 次要问题:

  • Why does the above code not have an error? 为什么上面的代码没有错误?

  • Are constructors inherited in TypeScript? 构造函数是否在TypeScript中继承?

When you have methods backed by an interface that includes parameters, the callers will be required to pass an argument, but the callees are not required to use it. 当具有由包含参数的接口支持的方法时,将要求调用者传递一个参数,但是不需要被调用者使用它。

So if the interface requires an argument to be passed, it is an error to omit it. 因此,如果接口要求传递参数,则忽略它是错误的。

interface Example {
    (x: number): void;
}

var giveMeANumber: Example = function (num: number) {

}

giveMeANumber(); // Error

But it is not an error to ignore the argument that has been passed. 但是,忽略已传递的参数并不是错误。

interface Example {
    (x: number): void;
}

var giveMeANumber: Example = function () {

}

giveMeANumber(15); // No Error

So in your case, you have defined that animals must be passed a string to their constructor. 因此,在您的情况下,您已经定义了动物必须将字符串传递给其构造函数。 All animals must have a string passed - this is the common signature you have defined. 所有动物都必须传递一个字符串-这是您定义的通用签名。 In the case of lions, the string is ignored (because it isn't needed). 如果是狮子,则忽略该字符串(因为不需要该字符串)。

In your example, if you look at the generated code, you'll see that when you omit a constructor in a sub-class, one is generated for you: 在您的示例中,如果查看生成的代码,则会看到在子类中省略构造函数时,会为您生成一个:

function Lion() {
    _super.apply(this, arguments);
}

And it uses the magical arguments , which means the name does in fact make its way down to the base class. 它使用了神奇的arguments ,这意味着该名称实际上确实可以使用到基类。 You can test this by serializing the lion: 您可以通过序列化狮子进行测试:

var lion = new Lion('Clarence');
alert(JSON.stringify(lion));

If you added a constructor to your lion class, you would be told by the compiler that you need to call super in the constructor, and when that you have to pass a string to it: 如果您在lion类中添加了一个构造函数,编译器会告知您需要在该构造函数中调用super ,以及何时必须向其传递字符串:

class Lion extends Animal {
    constructor() {
        super('HARD-CODED');
    }

    kind() : String { return "lion" ; }
}

So your type is accurate, but TypeScript applies this information pragmatically, and with respect paid to JavaScript paradigms. 因此您的类型是准确的,但是TypeScript务实地应用了此信息,并尊重JavaScript范例。

First of all, change the String types to be string . 首先,将String类型更改为string string is for the primitive type while String refers to the String interface. string是原始类型,而StringString接口。

Are constructors inherited in TypeScript? 构造函数是否在TypeScript中继承?

Yes. 是。 You can do a quick test to see: 您可以进行快速测试以查看:

abstract class Animal {
    constructor(name: string) {}
}

class Lion extends Animal {
}

new Lion(); // error

How do I write a type that describes objects which are classes that extend a given class and provide a constructor with a given parameter list? 如何编写一种描述对象的类型,这些对象是扩展给定类并为构造函数提供给定参数列表的类?

The way you are doing it is fine. 您的操作方式很好。 It will always error for the return type specified in the new signature, which is sufficient for most scenarios. 对于新签名中指定的返回类型,它总是会出错,这对于大多数情况来说已经足够了。 For example, passing it a class with a different structure from Animal will error. 例如,将与Animal不同结构的类传递给它会出错。

As you've discovered though, it doesn't error in some scenarios and you've discovered one of them. 正如您所发现的那样,在某些情况下它不会出错,并且您已经找到其中一种。 It seems to not error when the return type of the new signature matches, but the number of parameters is different. 当新签名的返回类型匹配时,似乎没有错误,但是参数的数量不同。 That's why it doesn't error when you remove the constructor. 这就是为什么在删除构造函数时它不会出错。 Note though, that it will error if the return type matches, but at least one of the parameters has a different type. 但是请注意,如果返回类型匹配,但至少一个参数具有不同的类型,它将出错。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM