Letharion
Letharion

Reputation: 4229

Declared class breaks type checking in typescript

I'm trying to type a callback, and I thought I had it working. But I just realized that I recently broke my definitions when I added a "declare class".

In the code below, I wouldn't expect 1 to be acceptable as either of ValidationError or Error, but I get no warning for ValidationError, why not?

Playground code

type CallbackBroken = {
  (error: ValidationError): void;
  (error: null, value: string): void;
}

type CallbackWorking = {
  (error: Error): void;
  (error: null, value: string): void;
}

function doThings1(c: CallbackBroken) {
    c(null, 'b');
    c(1);
}

function doThings2(c: CallbackWorking) {
    c(null, 'b');
    c(1);
}

declare class ValidationError {
    constructor(errorCode: number);
}

Upvotes: 0

Views: 95

Answers (1)

jcalz
jcalz

Reputation: 328187

The instance type ValidationError is structurally compatible with {}, since you have declared no instance properties for it, and only instance members are checked when the compiler checks if a value is compatible with a class. And since 1 is a valid {}, it compiles fine.

Edit: The {} type is the "empty type" or "empty interface". It has no declared properties, which places almost no restrictions on the values that satisfy it. (Only null and undefined are not compatible with {}). In TypeScript, a value of one type is A to a variable of type B, as in:

declare const a: A; // value of type A
const b: B = a; // assigned to variable of type B

if all the declared properties in type B are equal to (or subtypes of) the same-named properties in type A. If B is {}, you won't find a single property in any type A that conflicts. So this is fine:

const a = 1;  // value of type 1, has all the properties of Number instances
const b: {} = a; // assigned to variable of type {}.

The reverse is not fine, though:

const a = {}; // value of type {}
const b: 1 = a; // error, Type '{}' is not assignable to type '1'.

Does a ValidationError have any methods or properties? If you add one to the class declaration it should fix your problem. For example, if ValidationError implements Error, then you can change it to this:

declare class ValidationError implements Error {
    constructor(errorCode: number);
    name: string;
    message: string;
    stack?: string;
}

or as @err1100 suggests, since Error is also the name of a constructor of Error objects, this:

declare class ValidationError extends Error {
    constructor(errorCode: number);
}

And it should behave as you expect.

Hope that helps; good luck!

Upvotes: 2

Related Questions