约束索引签名(使用泛型)以使 sub-properties 具有匹配的类型
Constraining an index signature (using generics) to make sub-properties have matching types
在 TypeScript (v4.5.4) 中,我试图通过索引签名定义一个 object 类型。
我希望 TypeScript 强制 object 中的某些 sub-properties 具有匹配类型,但允许这些类型在 top-level 属性之间变化。
在下面的 (non-working) 示例中,我希望所有快乐的 driver 都能驾驶他们最喜欢的车型。
driver 最喜欢的车型与其实际车型不匹配会导致 TypeScript 编译器错误。
type CarType = 'Minivan' | 'Sports car' | 'Sedan' ; // | 'Pickup truck' |, etc. Imagine this union type has many possible options, not just three.
type Car = {
carType: CarType
// A car probably has many additional properties, not just its type, but those are left out of this minimal example.
// The solution should be resistant to adding additional properties on `Car` (and the `Driver` type below).
};
type Driver = {
favoriteCarType: CarType
car: Car
};
/**
* Happy drivers drive their favorite car type.
*/
const happyDrivers: { [name: string]: Driver } = {
alice: {
favoriteCarType: 'Minivan',
car: {
carType: 'Minivan', // ✅ Alice drives her favorite type of car.
},
},
bob: {
favoriteCarType: 'Sports car',
car: {
carType: 'Sedan', /* ❌ Bob doesn't drive his favorite type of car!
This currently does not throw a compiler error because my types are too permissive, but I want it to. */
},
},
};
我已经尝试将泛型应用于索引签名 and/or Car
and/or Driver
键入我能想到的所有方式,但我做不到让编译器强制执行约束,即 driver 的 favoriteCarType
必须与他们的 car
的 carType
.
完全匹配
你能帮帮我吗?
您要查找的是以下并集:
type Driver = {
favoriteCarType: "Minivan";
car: {
carType: "Minivan";
};
} | {
favoriteCarType: "Sports car";
car: {
carType: "Sports car";
};
} | {
favoriteCarType: "Sedan";
car: {
carType: "Sedan";
};
}
如果你使 Car
通用(汽车的类型是类型参数),你可以生成这个联合,我们使用自定义映射类型来创建联合的每个组成部分(然后得到一个联合索引回生成的映射类型):
type Car<T extends CarType = CarType> = { carType: T };
type Driver = {
[P in CarType]: {
favoriteCarType: P
car: { carType: P }
}
}[CarType];
使用映射类型让我走上正轨。请参阅下面的 HappyDriver
类型。
type CarType = 'Minivan' | 'Sports car' | 'Sedan' ; // | 'Pickup truck' |, etc. Imagine this union type has many possible options, not just three.
type Car = {
carType: CarType
// A car probably has many additional properties, not just its type, but those are left out of this minimal example.
// The solution should be resistant to adding additional properties on `Car` (and the `Driver` type below).
};
/**
* A driver may drive a car of any type, not just their favorite.
*/
type Driver = {
favoriteCarType: CarType
car: Car
};
/**
* A happy driver drives their favorite type of car.
*/
type HappyDriver = {
[C in CarType]: {
[K in keyof Driver]: Driver[K] extends Car
? { [K2 in keyof Car]: Car[K2] extends CarType
? C
: Car[K2]
} : Driver[K] extends CarType
? C
: Driver[K]
}
}[CarType]
const happyDrivers: { [name: string]: HappyDriver } = {
alice: {
favoriteCarType: 'Minivan',
car: {
carType: 'Minivan', // ✅ Alice drives her favorite type of car.
},
},
bob: {
favoriteCarType: 'Sports car',
car: {
carType: 'Sedan', /* Bob doesn't drive his favorite type of car, so this line causes a compiler error, which is what we want to happen. */
},
},
};
我已经将 Titian 的答案标记为正确答案,因为它帮助我找到了我正在寻找的答案。
在 TypeScript (v4.5.4) 中,我试图通过索引签名定义一个 object 类型。 我希望 TypeScript 强制 object 中的某些 sub-properties 具有匹配类型,但允许这些类型在 top-level 属性之间变化。
在下面的 (non-working) 示例中,我希望所有快乐的 driver 都能驾驶他们最喜欢的车型。 driver 最喜欢的车型与其实际车型不匹配会导致 TypeScript 编译器错误。
type CarType = 'Minivan' | 'Sports car' | 'Sedan' ; // | 'Pickup truck' |, etc. Imagine this union type has many possible options, not just three.
type Car = {
carType: CarType
// A car probably has many additional properties, not just its type, but those are left out of this minimal example.
// The solution should be resistant to adding additional properties on `Car` (and the `Driver` type below).
};
type Driver = {
favoriteCarType: CarType
car: Car
};
/**
* Happy drivers drive their favorite car type.
*/
const happyDrivers: { [name: string]: Driver } = {
alice: {
favoriteCarType: 'Minivan',
car: {
carType: 'Minivan', // ✅ Alice drives her favorite type of car.
},
},
bob: {
favoriteCarType: 'Sports car',
car: {
carType: 'Sedan', /* ❌ Bob doesn't drive his favorite type of car!
This currently does not throw a compiler error because my types are too permissive, but I want it to. */
},
},
};
我已经尝试将泛型应用于索引签名 and/or Car
and/or Driver
键入我能想到的所有方式,但我做不到让编译器强制执行约束,即 driver 的 favoriteCarType
必须与他们的 car
的 carType
.
你能帮帮我吗?
您要查找的是以下并集:
type Driver = {
favoriteCarType: "Minivan";
car: {
carType: "Minivan";
};
} | {
favoriteCarType: "Sports car";
car: {
carType: "Sports car";
};
} | {
favoriteCarType: "Sedan";
car: {
carType: "Sedan";
};
}
如果你使 Car
通用(汽车的类型是类型参数),你可以生成这个联合,我们使用自定义映射类型来创建联合的每个组成部分(然后得到一个联合索引回生成的映射类型):
type Car<T extends CarType = CarType> = { carType: T };
type Driver = {
[P in CarType]: {
favoriteCarType: P
car: { carType: P }
}
}[CarType];
HappyDriver
类型。
type CarType = 'Minivan' | 'Sports car' | 'Sedan' ; // | 'Pickup truck' |, etc. Imagine this union type has many possible options, not just three.
type Car = {
carType: CarType
// A car probably has many additional properties, not just its type, but those are left out of this minimal example.
// The solution should be resistant to adding additional properties on `Car` (and the `Driver` type below).
};
/**
* A driver may drive a car of any type, not just their favorite.
*/
type Driver = {
favoriteCarType: CarType
car: Car
};
/**
* A happy driver drives their favorite type of car.
*/
type HappyDriver = {
[C in CarType]: {
[K in keyof Driver]: Driver[K] extends Car
? { [K2 in keyof Car]: Car[K2] extends CarType
? C
: Car[K2]
} : Driver[K] extends CarType
? C
: Driver[K]
}
}[CarType]
const happyDrivers: { [name: string]: HappyDriver } = {
alice: {
favoriteCarType: 'Minivan',
car: {
carType: 'Minivan', // ✅ Alice drives her favorite type of car.
},
},
bob: {
favoriteCarType: 'Sports car',
car: {
carType: 'Sedan', /* Bob doesn't drive his favorite type of car, so this line causes a compiler error, which is what we want to happen. */
},
},
};
我已经将 Titian 的答案标记为正确答案,因为它帮助我找到了我正在寻找的答案。