1

I want to be able to suppress the compile time error that I get from Typescript where I use dot notation to access a property that the compiler doesn't know about. Access with bracket notation works, but dot notation gives a compile error.

My specific case is that I have added a custom matcher (call it 'toLookLike') to Jasmine using Angular. I can use it with brackets notation:

expect(something)['toLookLike'](otherthing);

but using dot notation would be much more readable

expect(something).toLookLike(otherthing);

But the matcher is added at runtime and the compiler doesn't know about it, so I get a compile error if I use the dot notation.

Is there a way to tell Typescript that dot notation is OK in this case? I'd be happy with doing it once, or on every line where the method is used. I do not of course have the ability to add toLookLike to the definition of the jasmine Matcher object.

I'm very familiar with the issues around allowing general dot notation for undefined properties, and in this specific case I'm prepared to take the risk. I don't need an explanation of why dot notation is prohibited in the general case. I've also read and understood this question.

DJClayworth
  • 26,349
  • 9
  • 53
  • 79

3 Answers3

4

If you add something to a typed library at runtime and want design-time typing for it, you should consider declaration merging, which augments the existing typings with your own addition.

I'm not sure how you are importing Jasmine, but assuming it's ambient/global (like you get if you run npm install --save-dev @types/jasmine), then the following might work for you:

declare global {
  namespace jasmine {
    interface Matchers<T> {
      // assuming that Expected<T> is the type of the param
      toLookLike(expected: Expected<T>): boolean;
    }
  }
}

Then you can use toLookLike() as desired at design time (just make sure you also do the runtime addition, or it will compile fine and then break at runtime).

expect(something).toLookLike(otherthing); // okay now

Hope that helps. good luck.

jcalz
  • 264,269
  • 27
  • 359
  • 360
3

I believe you are using jasmine but if not, this can be applied to other libraries as well. You can create your own typings. While the other answer should work, as you mentioned it doesn't add that much to readability. Other option is as follows:

  1. setup your custom typing looklike.matcher.d.ts

    declare module jasmine {
       interface Matchers {
          toLookLike(expected: any): boolean;
       } 
    }
    
  2. add this typings reference to your source code. just add this above your file

    /// <reference path="./looklike.matcher.d.ts"/> 
    
  3. then you can use it normally

    expect(something).toLookLike(otherthing);
    
Aivan Monceller
  • 4,636
  • 10
  • 42
  • 69
2

You could simply use a type cast:

(expect(something) as any).toLookLike(otherthing);
ideaboxer
  • 3,863
  • 8
  • 43
  • 62
  • A good idea I hadn't thought of, and thanks. It doesn't add a huge amount to the readability though. – DJClayworth Feb 28 '18 at 17:05
  • 1
    @DJClayworth Using `any` for something that isn't 'any' is never a good idea. – Estus Flask Feb 28 '18 at 17:49
  • @estus you are right it is a solution for exceptional cases because it not only "uglifies" the code but also turns off type checking for the affected expression. – ideaboxer Feb 28 '18 at 18:04