Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Null is usually not added to a variable's list of types #45

Open
Irelynx opened this issue Jun 23, 2022 · 4 comments
Open

Null is usually not added to a variable's list of types #45

Irelynx opened this issue Jun 23, 2022 · 4 comments
Assignees
Labels
bug Something isn't working in-progress
Milestone

Comments

@Irelynx
Copy link

Irelynx commented Jun 23, 2022

Hello.

I have tried to make some fields of class nullable, but can't get any information about allowed "nullable" behavior (same works with types, functions and interfaces too).

After a little investigation of the behavior, it turned out that the null type is added to field types in some cases. Here is an example code (ts) and "compiled" code (js):

import { NullLiteral } from "typescript";
// ...
class Test3 {
    testString1: string | null = null;
    testString2: null | string = null;
    testNull: null = null;
    testNumber: number | null = null;
    testUndefined: undefined | null = null;
    testDate: Date | null = null;
    testNullLiteral: NullLiteral | null = null;
    testBoolean: boolean | null = null;
    testAny: any | null = null;
    testInterface: Test2 | null = null;
    testPromise: Promise<string | number | null> | null = null;
    testMethod(arg: string | null): string | null {
        return arg;
    }
}
_ßr.Type.store.set(31, { n: "test3", k: 3, types: [
    _ßr.Type.store.wrap({ n: "string", k: 2, ctor: function () {return Promise.resolve(String);} }),
    _ßr.Type.store.wrap({ n: "number", k: 2, ctor: function () {return Promise.resolve(Number);} })
], union: true, inter: false });

_ßr.Type.store.set(99,  { n: "testDate", k: 3, types: [_ßr.Type.store.getLazy(99), _ßr.Type.store.wrap({ n: "null", k: 2 })], union: true, inter: false });
_ßr.Type.store.set(100, { n: "testNullLiteral", k: 3, types: [_ßr.Type.store.getLazy(100), _ßr.Type.store.wrap({ n: "null", k: 2 })], union: true, inter: false });
_ßr.Type.store.set(112, { n: "Promise", k: 2, args: [_ßr.Type.store.get(31)] });

_ßr.Type.store.set(95, { k: 1, n: "Test3", fn: "ts-api-test/test.ts:Test3#95", props: [
    { n: "testString1", t: _ßr.Type.store.wrap({ n: "string", k: 2, ctor: function () {return Promise.resolve(String);} }), am: 2, acs: 0, ro: false, o: false }, 
    { n: "testString2", t: _ßr.Type.store.wrap({ n: "string", k: 2, ctor: function () {return Promise.resolve(String);} }), am: 2, acs: 0, ro: false, o: false }, 
    { n: "testNull", t: _ßr.Type.store.wrap({ n: "null", k: 2 }), am: 2, acs: 0, ro: false, o: false }, 
    { n: "testNumber", t: _ßr.Type.store.wrap({ n: "number", k: 2, ctor: function () {return Promise.resolve(Number);} }), am: 2, acs: 0, ro: false, o: false }, 
    { n: "testUndefined", t: _ßr.Type.store.wrap({ n: "null", k: 2 }), am: 2, acs: 0, ro: false, o: false }, 
    { n: "testDate", t: _ßr.Type.store.get(99), am: 2, acs: 0, ro: false, o: false }, 
    { n: "testNullLiteral", t: _ßr.Type.store.get(100), am: 2, acs: 0, ro: false, o: false }, 
    { n: "testBoolean", t: _ßr.Type.store.wrap({ n: "boolean", k: 2, ctor: function () {return Promise.resolve(Boolean);} }), am: 2, acs: 0, ro: false, o: false },
    { n: "testAny", t: _ßr.Type.store.wrap({ n: "any", k: 2 }), am: 2, acs: 0, ro: false, o: false }, 
    { n: "testInterface", t: _ßr.Type.store.get(88), am: 2, acs: 0, ro: false, o: false }, 
    { n: "testPromise", t: _ßr.Type.store.get(112), am: 2, acs: 0, ro: false, o: false }
], meths: [
    { n: "testMethod", params: [
        { n: "arg", t: _ßr.Type.store.wrap({ n: "string", k: 2, ctor: function () {return Promise.resolve(String);} }), o: false }
    ], rt: _ßr.Type.store.wrap({ n: "string", k: 2, ctor: function () {return Promise.resolve(String);} }), tp: [], o: false, am: 2 }
], ctors: [{ params: [] }], ctor: function () {return Promise.resolve(Test3);} });

As you can see, only types for testNull, testDate, testNullLiteral are shown as they should.


Also, is the behavior of testDate correct?

In testDate recursive reference specified to type testDate (_ßr.Type.store.getLazy(99))


tst-reflect: 0.7.5
tst-reflect-transformer: 0.9.10

@Hookyns Hookyns added the bug Something isn't working label Jun 24, 2022
@Hookyns
Copy link
Owner

Hookyns commented Jun 24, 2022

TY @Irelynx for the issue.
I'll look into it.

Yes, testDate is ttly wrong.

@Hookyns
Copy link
Owner

Hookyns commented Aug 21, 2022

This is caused by the strictNullChecks tsconfig option. It is disabled by default and when it is disabled, you can assign null into many types.

Such as:

class Foo {
	testString1: string = null;
	testNumber: number = null;
	testBoolean: boolean = null;
}

const x = new Foo();
x.testString1 = null;
x.testNumber = null;
x.testBoolean = null;

That is valid TS code with default tsconfig (with strictNullChecks disabled).

So when you have

class Foo {
	testString1: string | null = null;
	testNumber: number | null = null;
	testBoolean: boolean | null = null;
}

it is the same type, cuz those nulls are stripped off. Compiler throws it away with disabled strictNullChecks.

Solution is to enable the strictNullChecks option.

There is a way how to get that information anyway (that there is union with null), but it is quite complicated and not reliable.

@Hookyns Hookyns self-assigned this Aug 23, 2022
@Hookyns
Copy link
Owner

Hookyns commented Aug 26, 2022

This must be handled so the reflection keeps standard behavior, no matter what the strictNullChecks option is.
So with strictNullChecks: false (default) every type will be union with null.

So not just fooProp: string | null; will be string | null but fooProp: string; will be string | null too.

But this has big impact and it is quite complicated to implement this into currect version so it will be in the next version.

@Hookyns Hookyns added this to the v1.0.0-alpha milestone Aug 26, 2022
@Irelynx
Copy link
Author

Irelynx commented Sep 1, 2022

Thanks a lot for your investigation!

I will try change the tsconfig and test my code as soon as I have time for it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working in-progress
Projects
None yet
Development

No branches or pull requests

2 participants