TypeScript
TypeScript copied to clipboard
Cannot circumvent weak type detection in class
TypeScript Version: 3.8.3
I understand the idea behind weak type detection. But there are cases where I want an interface with all-optional members, and I want to implement it without any of these members. I'm looking for an idiomatic way of telling TypeScript that this is intentional.
Search Terms: weak type detection
Code
interface Model {
empty?: boolean;
title?: string;
[key: string]: unknown; // only to prevent weak type detection
}
class MyModel implements Model {
id = 42;
}
Expected behavior:
The indexer in the Model interface prevents weak type detection as documented in the handbook.
Note: The syntax suggested in the handbook is [propName: string]: {}. This effectively forces all properties to be of object type, which doesn't make sense to me. So I went with unknown instead, as suggested here.
Actual behavior:
TypeScript gives me this error:
Class 'MyModel' incorrectly implements interface 'Model'. Index signature is missing in type 'MyModel'. (2420)
Playground Link: Link
Related Issues:
You can use // @ts-ignore. Why are you intending to do this?
We use an architecture where any object can be used as a model. But there are a number of properties that, if present on a model, have specific meaning. Some models implement all of these properties, some implement a subset, some implement none.
I understand that I can use ts-ignore. But from where I stand, TypeScript's weak type detection is just a heuristic that may or may not point to an actual error. So I'm looking for an idiomatic way of telling TypeScript that I know what I'm doing.
I had the same use case and found that any seems to work as the property type.
interface Model {
empty?: boolean;
title?: string;
[key: string]: any; // only to prevent weak type detection
}
@StevenGBrown This seems to work indeed. At the same time, however, it can make code highly unsafe. Consider the following:
interface Model {
empty?: boolean;
title?: string;
[key: string]: any; // only to prevent weak type detection
}
class MyModel implements Model {
id = 42;
}
const model: Model = new MyModel();
const test: number = model.noSuchProperty;
Giving the Model interface an indexer with any type effectively allows you to read and write non-existing Model properties without any type checks. That's why I chose unknown (which sadly didn't work).
Ah yes that's unfortunate.
Same issue here. We've also a model interface for a dialog, which can have only optional properties / methods.
it works fine and unsafe if we use any
export interface IDialogModel {
onOk?(): void;
onCancel?(): void;
[key: string]: any;
}
// ok
class DialogModel implements IDialogModel {
onCancel(): void {
/* ignore */
}
onOk(): void {
/* ignore */
}
}
but if we use unknown we get same error message as in issue description
export interface IBsDialogModel {
onOk?(): void;
onCancel?(): void;
[key: string]: unknown;
}
// error: Index signature for type 'string' is missing in type 'DialogModel'. ts(2420)
class DialogModel implements IDialogModel {
onCancel(): void {
/* ignore */
}
onOk(): void {
/* ignore */
}
}