我正在尝试创建一个具有2个参数的函数,其中第二个参数的类型基于第一个参数的类型。我看到几个线程可能会重复这个问题,但是键入语法可能变得如此复杂,以至于我已经摆弄了好一阵子,无法正确解决问题。
class Mammal { }
class Bird { }
function stuff(classType: typeof Mammal | typeof Bird, arg: number or string depends on first param) {
if (classType extends Mammal) {
console.log(arg * 5);
} else {
console.log(arg.split(" "))
}
}
stuff(Mammal, 5); // want this to be valid
stuff(Bird, "Hello world"); // want this to be valid
stuff(Bird, 5); // want this to be invalid
答案 0 :(得分:1)
class Mammal {
static type = 'mammal' as const
}
class Bird {
static type = 'bird' as const
}
function stuff<
M extends typeof Mammal | typeof Bird,
A extends (M extends (typeof Mammal) ? number : string)>
(classType: M, arg: A) {
if (classType.type === 'mammal') {
console.log((arg as number) * 5);
} else {
console.log((arg as string).split(" "))
}
}
stuff(Mammal, 5); // ok
stuff(Mammal, 'a'); // error
stuff(Bird, "Hello world"); // ok
stuff(Bird, 1); // error
TS是结构化类型的语言,这意味着,如果您有两个具有相同定义的类,则它们对于TS来说是相同的,因为它们之间没有结构上的区别。以下是该声明的证明:
class Mammal {}
class Bird {}
type MammalIsBird
= Mammal extends Bird
? Bird extends Mammal
? true
: false
: false // evaluates to true, Mammal and Bird are equal
这就是为什么要区分Mammal
和Bird
的原因,我们需要创建一些静态属性以具有区别。
另一件事是,例如当我们询问Mammal
的此属性时,将没有任何属性,因为Mammal
具有所有非静态属性,因此我们需要依次使用typeof Mammal
具有与静态接口的接口。这就是为什么在实现中我使用typeof M
而不是M
的原因。
一些重要信息:
M extends typeof Mammal | typeof Bird
表示我们允许或分类“哺乳动物”或“鸟类”,如果我只说Mammal | Bird
,则意味着我要获取该类的实例,而不是类本身A extends (M extends (typeof Mammal) ? number : string)
-条件类型,如果我们将Mammal
类型设为M
,则有第二个参数number
,如果不是string
classType.type === 'mammal'
由于具有静态属性,我们可以在条件中使用它(arg as number)
类型断言,因为对第一个属性的判别不适用于第二个属性。您可以通过使用一种对象/数组类型的参数来避免类型声明。但是,我不建议您使用这种方法,不过您还是可以:
type MammalArgs = [typeof Mammal, number]
type BirdsArgs = [typeof Bird, string];
const isMammalClass = (x: MammalArgs | BirdsArgs): x is MammalArgs => x[0].type === 'mammal'
const isBirdArgs = (x: MammalArgs | BirdsArgs): x is BirdsArgs => x[0].type === 'bird'
function stuff<
M extends typeof Mammal | typeof Bird,
A extends (M extends (typeof Mammal) ? MammalArgs : BirdsArgs)>
(...args: A) {
if (isMammalClass(args)) {
console.log(args[1] * 5); // no type assertion
}
if (isBirdArgs(args)) {
console.log(args[1].split(" ")); // no type assertion
}
}
我在上面所做的是将我们的参数合并为一个类型[typeof Mammal, number] | [typeof Bird, string]
,以实现第一个参数的判别式与第二个参数的判别式之间的关系。两种类型都具有这种关系。问题是我需要创建两个类型保护,还需要直接使用数组,因为任何解构都会破坏我们的类型缩小。我会选择第一种带有类型断言的方法。