У вас есть несколько синтаксических ошибок в вашем коде, возможно, это было просто ради примера? Это должно выглядеть примерно так, чтобы работать правильно (без каких-либо изменений в намерениях):
class Comm {
_type: string;
}
class EMail extends Comm {
_type = 'email';
email: string;
}
class Phone extends Comm {
_type = 'phone';
phone: number;
}
function registerCallback(_type: string, fn: (comm: Comm) => void) {}
registerCallback('email', (e) => {}); // Correct
registerCallback('phone', (e) => {}); // Should be error
Вы можете сделать это несколькими различными способами. Одним из простых способов было бы объявить enum
и использовать его для строгого контроля над тем, какие значения _type
могут иметь (объявление его как _type: string
недостаточно для указания c):
Теперь вы получаете сообщение об ошибке каждый раз, когда пытаетесь использовать неправильную комбинацию _type
и fn
формы обратного вызова. Однако это довольно хрупко и легко сломается, если вы внесете какие-либо изменения в определения типов. Это также не очень DRY, и вам придется многократно повторять определения registerCallback
, если вы хотите его перегрузить. Кроме того, каждый раз, когда вы вызываете registerCallback
, вы должны передавать _type
и fn
как два отдельных аргумента, которые кажутся довольно избыточными.
Элегантное решение здесь - использовать Generics. Вы можете определить типы и registerCallback
таким образом, чтобы тип был определен в время использования заранее, но все еще имеет строгие защитные ограждения относительно того, как его можно использовать:
class Comm {}
class EMail extends Comm {
email: string;
}
class Phone extends Comm {
phone: number;
}
type CommUnion = EMail | Phone;
function registerCallback<T extends CommUnion>(fn: (e: T) => void) {}
registerCallback((e: EMail) => e.email); // Correct
registerCallback((e: Phone) => e.phone); // Correct
registerCallback<EMail>((e) => e.email); // Correct
registerCallback<Phone>((e) => e.phone); // Correct
registerCallback((e: { phone: 123456789 }) => e.phone); // Correct
registerCallback((e: { email: 'me@me.com' }) => e.email); // Correct
registerCallback((e: { email: 'me@me.com' }) => e.phone); // Should be error (types don't match)
registerCallback<Phone>((e) => e.email); // Should be error (types don't match)
registerCallback((e: { foo: 'foo' }) => e.foo); // Should be error (e is improper type)
registerCallback((e) => e.email); // Should be error (no type specified)
Посмотрите, сколько чище это выглядит! Я настоятельно рекомендую подход Generi c, если вы можете. Они могут быть немного хитрыми, поэтому прочитайте его, чтобы узнать больше о том, как он работает и чувствовать себя комфортно. :)