#typescript #typescript-typings
#typescript #typescript-типизации
Вопрос:
Я не в состоянии полностью понять использование ключевого слова extends в случае типов объединения. Вот фрагмент кода, объясняющий возникшую у меня путаницу.
class SomeClass {
someClassProp: string;
};
class SomeExtendedClass extends SomeClass {
someExtendedClassProp: string;
};
function someClassFunction<T extends SomeClass>(args: T): T {
return args;
};
let someClass: SomeClass, someExtendedClass: SomeExtendedClass;
someClassFunction(someClass);
someClassFunction(someExtendedClass); // Works just fine(case 1)
type someType = 'a' | 'b';
type someExtendedType = someType | 'c';
function someTypeFunction<T extends someType>(args: T): T {
return args;
};
let someType: someType, someExtendedType: someExtendedType;
someTypeFunction(someType);
someTypeFunction(someExtendedType); // Gives me an error(case 2)
Итак, мне было интересно, почему принимаются такие дизайнерские решения и каковы их последствия.
изменение function someTypeFunction<T extends someType>(args: T): T {return args;};
на function someTypeFunction<T extends someExtendedType>(args: T): T {return args;};
работает, но я не могу понять, как эта штука на самом деле работает.
РЕДАКТИРОВАТЬ 1
type someType = 'a' | 'b';
type someOtherType = 'c';
type someUnionType = someType | someOtherType;
type someTypeCore<T extends someUnionType> = { type: T };
type someTypeObj<T extends someType> = { a: string } amp; someTypeCore<T>;
type someOtherTypeObj<T extends someOtherType> = { b: string, c: string } amp; someTypeCore<T>;
function typeAssertion<T extends someUnionType>(args: someTypeCore<T>): args is someTypeObj<T> {
return (args as someTypeObj<T>).a !== undefined; // Gives an error
};
function someTypeFunction<T extends someUnionType>(args: someTypeCore<T>): T {
if (typeAssertion(args)) {
// Do something for someTypeObj
} else {
// Do Something for someOtherTypeObj
};
return args.type;
};
Как нам решить эту проблему.
Комментарии:
1. Когда класс расширяет другой класс, это означает, что он наследует все, что там было, плюс может добавить что-то еще. Когда вы используете union — вы не расширяете тип, вы просто разрешаете передавать какой-либо другой независимый тип.
2. итак , вопрос в том , почему случай 2 терпит неудачу ?
3. Потому что
type someExtendedType = someType | 'c';
не распространяетсяsomeType
.
Ответ №1:
extends
В функции сохраняется T
значение подтипа указанного типа. То, что означает подтип, может быть немного неожиданным для объединений.
Вы должны подумать о том, что на самом деле означает отношение подтипа. Когда мы говорим, что это SomeExtendedClass
подкласс SomeClass
, подразумевается, что любой экземпляр SomeExtendedClass
также является экземпляром SomeClass
Итак, набор всех SomeClass
экземпляров содержит набор всех SomeExtendedClass
экземпляров.
Но если мы применим этот взгляд на подтипы к объединениям, мы увидим, что объединение с меньшим количеством членов ( "a" | "b"
) на самом деле является подтипом объединения с большим количеством членов ( "a" | "b" | "c"
), поскольку все экземпляры "a" | "b"
являются экземплярами "a" | "b" | "c"
.
Мы интуитивно думаем, что подтип — это тот, в котором «больше материала», но когда мы думаем о объединениях, эта интуиция подводит нас, нам нужно подумать об отношениях подтипов в терминах множеств.
Комментарии:
1. Хммм, я не понимаю. Окружность SomeClass должна быть наоборот. Экземпляр SomeExtendedClass должен быть большим кругом, инкапсулирующим экземпляр SomeClass, поскольку экземпляр SomeExtendedClass будет обладать всеми свойствами экземпляра SomeClass
2. @AmolGupta Ты неправильно смотришь на это. Внешний круг содержит все экземпляры
SomeClass
в этом круге некоторые экземпляры, которые являютсяSomeClass
такжеSomeExtendedClass
экземплярами, но не все. НаборSomeClass
экземпляров больше и содержит набор всехSomeExtendedClass
экземпляров. Не имеет значения, у кого больше свойств, диаграммы иллюстрируют наборы. en.wikipedia.org/wiki/Set_ (математика)3. не могли бы вы, пожалуйста, взглянуть на приведенную выше правку и как я могу ее разрешить?
4. @AmolGupta Ошибки, которые вы получаете, кажутся мне допустимыми. Если
someTypeObj
есть параметр типа, который может быть"a" | "b"
, почему его допустимо передавать"c"
? Кроме того, существует otypeAsserion
синтаксис .. только для защиты типа. Не уверен, как вы можете конкретно исправить этот код .. мне это кажется непоследовательным, и без более реального примера я не уверен, как это исправить.5. Итак, утверждение типа было бы в операторе if, и я хотел бы условно перейти на основе
type
того, что было передано. Пожалуйста, посмотрите правку.