【发布时间】:2017-12-13 01:53:59
【问题描述】:
目标
我最好尝试在项目中强制输入,以便任何实现 Animal 的人都必须提供此函数并确保该函数只能将 Woof 或 Meow 作为唯一参数,并且必须只返回其中一个呜呜声或喵喵声。
初步尝试
这是一些与我在 VSCode 中获得的代码(使用 TypeScript 2.4.1)类似(名称除外)的示例代码:
class Woof {
constructor(private a: boolean = true) {}
}
class Meow {
constructor(private b: string = "abc") {}
}
class Dog implements Animal {
doSomething(sound: Woof): Woof {
return new Woof();
}
}
class Cat implements Animal {
doSomething(sound: Meow): Meow {
return new Meow();
}
}
interface Animal {
doSomething: <T extends Woof | Meow>(input: T) => T;
}
如果我删除它 in the TypeScript playground,它会生成我期望的 JavaScript 而不会出错。
但是,在 VSCode 中,我在 Dog 和 Cat 类下都有一条红色波浪线,鼠标悬停会读取以下所有内容:
类“狗”错误地实现了接口“动物”。
属性“doSomething”的类型不兼容。
类型“(声音:Woof)=> Woof”不能分配给类型“(输入:T)=> T”。
参数“sound”和“sound”的类型不兼容。
类型“T”不能分配给类型“Woof”。
键入'Woof | Meow' 不能分配给类型 'Woof'。
类型 'Meow' 不能分配给类型 'Woof'。
类型“喵”中缺少属性“a”
每个错误都是嵌套的,因此很难在此处准确显示格式,但这似乎表明我在扩展泛型时不能只指定 Woof 或 Meow 类型,因为未实现私有属性两种类型(看起来类似于another question here)。尽管在文档中没有看到任何表明这是非法的东西(同样,在操场上,这似乎没有问题)。但是,与那个问题不同的是,我不希望泛型同时扩展这两种类型,而是仅限于其中一种。
阅读关于交集类型的帖子中的描述,它显示“联合类型 A | B 表示一个类型为 A 或类型 B 的实体,而交集类型 A & B 表示一个类型为 A 的实体和类型 B"。读到这里,我肯定想要一个联合类型,但这就是我上面所说的,而且似乎没有。
alternate 类下的波浪线中的类型被翻转,但消息是相同的。
当我在输出中使用“tsc”实际构建项目时,我也会收到此错误。
替代尝试 - 用每个实现的接口替换类型
我还尝试实现以下内容,希望它可以消除私有属性在 Woof 和 Meow 类之间匹配的奇怪需求(以防在指定每个类时出现意外情况并不起作用):
interface Sound {
}
class Woof implements Sound {
constructor(private a: boolean = true) {}
}
class Meow implements Sound {
constructor(private b: string = "abc") {}
}
class Dog implements Animal {
doSomething(sound: Woof): Woof {
return new Woof();
}
}
class Cat implements Animal {
doSomething(sound: Meow): Meow {
return new Meow();
}
}
interface Animal {
doSomething: <T extends Sound>(input: T) => T;
}
对于每个类,我都得到与以前完全相同的错误。
半工作版
现在,如果我完全消除泛型并仅使用上述替代方案中的接口,它可以解决部分目标,因为我可以将参数和结果限制为实现接口的类,但这并不完全理想因为它不会阻止其他开发人员传入一个接口实现并尝试返回另一个。
interface Sound {
}
class Woof implements Sound {
constructor(private a: boolean = true) {}
}
class Meow implements Sound {
constructor(private b: string = "abc") {}
}
class Dog implements Animal {
doSomething(sound: Woof): Woof {
return new Woof();
}
}
class Cat implements Animal {
doSomething(sound: Meow): Meow {
return new Meow();
}
}
interface Animal {
doSomething: (input: Sound) => Sound;
}
我更喜欢使用泛型并将 T 限制为一种实现的类型安全。关于如何使用泛型和 Typescript 在操场之外实际使用的语法来解决这个问题的任何想法?
【问题讨论】:
-
用重载代替泛型怎么样?
-
@unional 我宁愿不这样做,因为我希望其他开发人员简单地被约束(通过类型系统)来实现 Animal 接口并被约束输入一种类型并输出相同的类型,每个特定选项都在 Animal 的函数定义本身中。虽然我可以合并类型,但我不能将这些约束合并到泛型本身上似乎很奇怪。
标签: typescript typescript-generics