#I'm able to call a method on an undefined value

18 messages · Page 1 of 1 (latest)

mossy flume
#

I am able to call a method on the return value of a function with | undefined in the return type, which I didn't think would be possible.

I have a helper function with the following signature:

export const as_trait = <T extends keyof Traits>(o_: object, symbol: T): Traits[T] | undefined => { ... }

At the very top of the function I even have this guard clause:

  if ( ! o_.hasOwnProperty(symbol) ) {
      return undefined;
  }

Traits is an interface type. The idea is I have several modules that may add symbols to this interface that map to certain types.

Here's what confuses me: I can call as_trait, pass it a value of type any for the o_ parameter, and then call a method on the return value of as_trait without ever checking if it's undefined.

i.e. this compiles:

const whatever = this.context[entry.key];
const initable = as_trait(whatever, TInitable);
// if ( initable === undefined ) continue;
await initable.init();

Why does this happen? Can I get typescript to enforce the undefined check as I expected it to?

I'm not interested in "why do you want to do this?" or "just do it my way instead". I want to understand this problem that I find myself in.

#

I'm able to call a method on an undefined value

tame wren
#

Please post a sample that demonstrates the issue

mossy flume
#

What's missing?

merry drift
#

not sure if this sufficiently replicates your problem but the playground is erroring as it should

buoyant fiberBOT
#
xibread#0

Preview:```ts
interface Traits {
foo: {init(): void}
bar: {baz(): void}
}

declare const as_trait: <T extends keyof Traits>(
o_: object,
symbol: T
) => Traits[T] | undefined

declare const whatever: any
const initable = as_trait(whatever, "foo")

initable.init()```

mossy flume
#

This is my minimum reproducable example:

const TFoo = Symbol('TFoo');
const TBar = Symbol('TBar');

export interface Things {
    [TFoo]: {
        method()
    },
    [TBar]: {}
}

export const as_trait = <T extends keyof Things>(o: object, symbol: T): Things[T] | undefined => {
    return undefined;
}

const object_with_bar = {
    [TBar]: {},
}

// Try to get TFoo from object_with_bar, which should return undefined
const thingy = as_trait(object_with_bar, TFoo);

thingy.method();
#

It lets me call the method even though I'm explicitly returning undefined and my return type is Things[T] | undefined.

#

Oddly enough the playground detects this, but vscode and the typescirpt compiler do not.

merry drift
#

Do you have strictNullChecks enabled in your tsconfig?

mossy flume
#

That's the default? Yeah, turns out in the annoying process of figuring out how to get tsc to work with am monorepo, the root tsconfig having "struct": true in it doesn't matter.

#

How do I mark this solved?

#

Wow, I can't seem to get over this. Why would anybody want to use typescirpt and also not have that enabled? I can't imagine why that's even an option.

edgy tundra
#

@mossy flume Strictness rules are opt-in becauase otherwise it'd make breaking changes whenever they're added.

#

And also some people like to migrate codebases from JS by turning TS on with strict: false.

mossy flume
#

That makes sense, thanks

edgy tundra
#

They're a good source for 'starter' tsconfigs and if you aren't aware there are some strictness options that aren't included in strict: true