typescript3.0 - TypeScript: bir string literal özelliğine dayanarak birleşim türü üyesini genel olarak çıkarma
TypeScript (v3.2.2) bana, her biri tür koruyucu olarak kullanılabilecek benzersiz bir string literal özelliğe sahip olan arayüzlerin birleşimini tanımlamama izin veriyor, örneğin.
type Device = Laptop | Desktop | Phone;
interface Laptop {
type: 'Laptop';
countDriveBays: number;
hasTouchScreen: boolean;
}
interface Desktop {
type: 'Desktop';
countDriveBays: number;
}
interface Phone {
type: 'Phone';
hasTouchScreen: boolean;
}
function printInfo(device: Device) {
if (device.type === 'Laptop') {
// device: Laptop
console.log(
`A laptop with ${device.countDriveBays} drive bays and ${
device.hasTouchScreen ? 'a' : 'no'
} touchscreen.`,
);
} else if (device.type === 'Desktop') {
// device: Desktop
console.log(`A desktop with ${device.countDriveBays} drive bays.`);
} else {
// device: Phone
console.log(`A phone with ${device.hasTouchScreen ? 'a' : 'no'} touchscreen.`);
}
}
Genel bir şekilde bir isDeviceType fonksiyonu yazmak istiyorum:
const isDeviceType = <T extends Device['type']>(type: T) => {
return (device: Device): device is DeviceOf<T> => device.type === type;
}
// e.g.
const isPhone = isDeviceType('Phone');
isPhone({ type: 'Phone', hasTouchScreen: true }); // true
Ancak, DeviceOf türünü tanımlama şeklim oldukça ayrıntılı çünkü birleşim içindeki her türü tek tek listeliyor:
type DeviceOf<Type extends Device['type']> =
Type extends Laptop['type'] ? Laptop :
Type extends Desktop['type'] ? Desktop :
Type extends Phone['type'] ? Phone :
never;
DeviceOf için daha kısa bir tanım yolu var mı? Bunları denedim:
type DeviceOf<Type extends Device['type']> =
(infer D)['type'] extends Type ? D : never;
// TS2536: Type '"type"' cannot be used to index type 'D'.
// TS1338: 'infer' declarations are only permitted in the 'extends' clause of a conditional type.
// TS6133: 'D' is declared but its value is never read.
type DeviceOf<Type extends Device['type']> =
(infer D) extends Device
? D['type'] extends Type
? D
: never
: never;
// TS1338: 'infer' declarations are only permitted in the 'extends' clause of a conditional type.
// TS6133: 'D' is declared but its value is never read.
// TS2304: Cannot find name 'D'.
Benim izlenimim, TS1338 hatasının sınırlayıcı faktör olduğu ve bu nedenle DeviceOf'u mevcut TypeScript sürümünde genel bir şekilde tanımlamanın imkansız olduğu yönünde.
Cevap #1
Sadece infer anahtar kelimesini kullanmadan, koşullu tipleri kullanarak alternatif bir yol buldum:
type FindByType<Union, Type> = Union extends { type: Type } ? Union : never;
type DeviceOf<Type extends Device['type']> = FindByType<Device, Type>;
type Result = DeviceOf<'Laptop'>;
Ryan Cavanaugh'ın buradaki yorumuna dayanarak: https://github.com/Microsoft/TypeScript/issues/17915#issuecomment-413347828
Cevap #2
Anladım. "if"i iki kez uygulaman gerekiyor, biri infer türünü oluşturmak için, ikincisi infer türünün device'a genişleyip genişlemediğini kontrol etmek için. Yalnızca D extends Device dalında D['type'] kullanabileceksin.
type DeviceOf<Type extends Device['type']> =
Device extends (infer D) ?
D extends Device ?
D['type'] extends Type ? D : never : never : never;
type Result = DeviceOf<'Laptop'>;
Oyun alanı
