Что на самом деле означает «extends» в typescript?

#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" ? Кроме того, существует o typeAsserion синтаксис .. только для защиты типа. Не уверен, как вы можете конкретно исправить этот код .. мне это кажется непоследовательным, и без более реального примера я не уверен, как это исправить.

5. Итак, утверждение типа было бы в операторе if, и я хотел бы условно перейти на основе type того, что было передано. Пожалуйста, посмотрите правку.