#typescript
#машинописный текст
Вопрос:
У меня есть следующий код, который просто возвращает ошибку.
class Foo {
public static logBar<T>(a: T): T {
console.log(a);
return a
}
}
class Bar extends Foo {
public static logBar<T extends number>(a: T): T {
console.log(a);
return a
}
}
Class static side 'typeof Bar' incorrectly extends base class static side 'typeof Foo'.
Types of property 'logBar' are incompatible.
Type '<T extends number>(a: T) => T' is not assignable to type '<T>(a: T) => T'.
Types of parameters 'a' and 'a' are incompatible.
Type 'T' is not assignable to type 'number'.(2417)
первоначально я думал, что это была общая ошибка, но я смог повторить это со следующим
class Foo {
public static logBar(a: string): string {
console.log(a);
return a
}
}
class Bar extends Foo {
public static logBar(a: number): number {
console.log(a);
return a
}
}
Class static side 'typeof Bar' incorrectly extends base class static side 'typeof Foo'.
Types of property 'logBar' are incompatible.
Type '(a: number) => number' is not assignable to type '(a: string) => string'.
Types of parameters 'a' and 'a' are incompatible.
Type 'string' is not assignable to type 'number'.(2417)
Является ли статическая функция с разными параметрами не перегружаемой в typescript? если да, то почему другие языки делают это постоянно? примеры в комментариях.
пожалуйста, обратите внимание, что работает следующее
class Foo {
public static logBar(a: string): string {
console.log(a 'Foo');
return a
}
}
class Bar extends Foo {
public static logBar(a: string): string {
console.log(a 'Bar');
return a
}
}
Foo.logBar('hello');
Bar.logBar('world');
и результаты с
helloFoo
worldBar
Ответ №1:
Экземпляр дочернего класса должен иметь возможность использоваться всякий раз, когда используется экземпляр базового класса. Это называется принципом подстановки Лискова, и он просит методы дочерних классов принимать в качестве входных данных все значения, принятые тем же методом базового класса в качестве входных данных (и, возможно, больше), и не возвращать значения, которые не может вернуть тот же метод базового класса. Тот же принцип также не позволяет дочернему классу ограничивать видимость свойства, унаследованного от базового класса (если оно находится public
в базовом классе, его нельзя изменить на protected
или private
в дочернем классе; допускается только наоборот).
Статический метод Foo.logBar()
может быть вызван string
, например, с параметром типа, поскольку нет никаких ограничений на общий параметр T
.
Но Foo
не может быть заменено на Bar
в выражении Foo.logBar('abc')
, поскольку 'abc'
является строкой и Bar.logBar()
ожидает аргумент типа number
(из <T extends number>
-за определения метода logBar()
в классе Bar
).
Это, на простом английском языке, то, что пытается сообщить вам сообщение об ошибке, которое вы процитировали.
T
в базовом классе может быть что угодно, но не все значения, которые он принимает в базовом классе, принимаются для него в дочернем классе; дочерний класс принимает только number
для T
.
Комментарии:
1. то, что вы говорите, имеет смысл для функций-членов. но я говорю о статических функциях. вы просто говорите о полиморфизме. статические функции не являются полиморфными или, по крайней мере, не должны быть.
2. вот пример, который я написал на c , который действительно быстро работает. что соответствует тем же строкам, что и функция выше. код
3. Классы JavaScript на самом деле являются функциями, а функции JavaScript являются объектами. Это означает, что «статические» методы TypeScript на самом деле являются свойствами объекта, следовательно, методами экземпляра. Дочерние классы JavaScript сохраняют ссылку на свой родительский класс; именно так они наследуют свойства родительского класса, и именно поэтому LSP, вероятно, реализован и для статических методов. Разные языки, разные способы реализации ООП.
4. я привел пример на C #, который делает то, о чем я говорю. gist.github.com вы можете скомпилировать и посмотреть, что это работает, используя dotnetfiddle.net
5. C # — это не TypeScript, даже если они оба от Microsoft (а C # был источником вдохновения для создателей TypeScript) 🙂 Я знаю, что компилятор TypeScript иногда раздражает, но он прав больше, чем мне хотелось бы признавать. Это защищает вас от ошибок, которые может быть сложнее обнаружить во время выполнения.
Ответ №2:
Если вы ориентируетесь на ES6 или новее, то это, вероятно, связано с тем, что компилятор TS генерирует классы ES в соответствии с вашими TypeScript. И согласно этому сообщению в блоге, классы ES6 наследуют статические члены при расширении других классов. Из-за такого поведения TS должен обеспечивать совместимость типов между статическими членами, чтобы предотвратить возможную путаницу типов во время выполнения.
Перегрузка и переопределение TS сопровождаются множеством предостережений по сравнению с другими объектно-ориентированными языками. На таком языке, как C # или Java (не script), приведенные выше примеры приведенных выше типов были бы полностью корректными. Foo
будет иметь один статический метод logBar
, который возвращает a string
, и Bar
будет иметь два статических метода — исходный logBar
и перегрузку , которая возвращает a number
. В TS это невозможно, поскольку JavaScript не поддерживает перегрузку. Для каждого имени свойства каждого объекта может быть только один метод, независимо от типа. Перегрузки методов в TS возможны только потому, что они существуют только во время компиляции. Компилятор удаляет определения перегрузки, и только реализация передается в JS. TS не допускает повторяющихся реализаций перегрузки. Поскольку оригинал logBar
наследуется, по сути, существуют две конфликтующие реализации одного и того же метода. Я полагаю, что вы увидели бы ту же ошибку, даже если бы методы не были статическими.
Комментарии:
1. Я обновил свой вопрос, чтобы отразить ваш комментарий. Статические функции наследуются, но это не означает, что они не могут быть переопределены.
2. В этом случае, я думаю, проблема просто в том, что вы пытаетесь переопределить
logBar
функцию, которая возвращает несовместимый возвращаемый тип. Даже с помощью обычного метода вы не можете переопределить метод, который возвращаетstring
, с помощью метода, который возвращаетnumber
. Если вы изменитеFoo.logBar
значение returnstring | number
, исчезнет ли ошибка?3. нет, все та же проблема typescriptlang
4. Я обновил свой ответ некоторой дополнительной информацией, которая может помочь. По сути, вы
logBar
перегружаете несовместимые типы, что недопустимо в TypeScript. Не имеет значения, что методы являются статическими, потому что они наследуются.5. я думаю, что перегрузка, о которой вы говорите, отличается от того, о чем я говорю. вы говорите об использовании полиморфизма, я говорю о статических функциях. я привел пример на C #, который делает то, о чем я говорю. gist.github.com вы можете скомпилировать и посмотреть, что это работает, используя dotnetfiddle.net