#typescript
#typescript
Вопрос:
Я хочу иметь интерфейс, подобный этому:
export interface Point {
readonly x: number;
readonly y: number;
readonly toString: never;
}
Я думал, что это будет работать следующим образом:
const p: Point = {x: 4, y: 5}; // OK
p.toString(); // triggers typescript error
Однако я получаю также эту ошибку в первой строке:
TS2322: Type '{ x: number; y: number; }' is not assignable to type 'Point'.
Types of property 'toString' are incompatible.
Type '() => string' is not assignable to type 'never'.
Есть ли возможность ограничить использование toString в каком-либо интерфейсе без необходимости писать утверждения типа, подобные const p: Point = {x: 4, y: 5} as Point;
везде?
Мой вариант использования: в настоящее время я переписываю то, что раньше было
class Point {
x: number;
y: number;
toString() {
return `${x} ${y}`;
}
}
для сопряжения объекта с сопутствующими функциями:
interface Point {
x: number;
y: number;
}
function pointToString({x, y}: Point) {
return `${x} ${y}`;
}
и я хочу, чтобы вызовы point.toString()
в старой кодовой базе вызывали ошибку, потому что в настоящее время они этого не делают, поскольку у каждого объекта в JS есть toString()
метод.
Комментарии:
1. Почему вы хотите, чтобы все эти вызовы приводили к ошибке, учитывая, что все в JS действительно поддерживается
toString
?2. @jonrsharpe Я думаю, что я достаточно ясно объяснил это в своем примере использования, я хочу ограничить использование Object.toString, когда объект действует в этом интерфейсе, поскольку это на 100% ошибка.
3.Почему это ошибка, хотя? Вы вообще этого не объяснили — у
Point
действительно естьtoString
метод, и если вы хотите переопределить этот класс, это уже разумный способ сделать это.4. по-старому: const p = новая точка(1, 1); вернуть p.toString(); по-новому: const p = {x: 1, y: 1}; вернуть pointToString(x); . По-новому, вызовы p.toString() являются ошибкой, поскольку они запускают метод toString прототипа объекта с другим выводом, вместо этого мы должны использовать pointToString, но я могу пропустить много подобных мест при рефакторинге старого кода. Если вы спрашиваете, почему я не переопределяю toString — мы переходим к функциональному подходу с простыми объектами, мы не хотим переопределять метод toString, мы вообще не хотим использовать этот метод, просто чтобы убедиться, что мы не вызываем его в устаревшем коде.
5. В любом случае, независимо от того, каков мой вариант использования и считаете ли вы его законным или нет, я думаю, что все еще остается актуальным вопрос, возможно ли писать интерфейсы на typescript, которые ограничивают доступ к встроенным методам объекта.
Ответ №1:
Я смог заставить это работать с unknown
типом:
type NullPrototype = Record<keyof Object, unknown>;
interface Point extends NullPrototype {
readonly x: number;
readonly y: number;
}
const p: Point = {x: 4, y: 5}; // OK
p.toString(); // TS error: Object is of type 'unknown'.
Я назвал это NullPrototype
потому что это, по сути, версия этого шаблона на уровне типа в vanilla JS:
const p = Object.create(null); // TS infers type `any`
p.x = 4;
p.y = 5;
p.toString(); // Fails at runtime, but passes type check???
Вот связанная с этим проблема на TypeScript GitHub, хотя я не думаю, что кто-то придумал это конкретное решение там