Dart language developers think that Typescript only imitates statically typed language. They don't say it in a direct manner.
Typescript is a statically typed language, built on a dynamically typed runtime. If you stay within typescript it's mostly typesafe (there is exactly one case where unions are treated non conservatively which can lead to bugs).
Most issues arise in Typescript because it usually interfaces with plain javascript, so while typescript is typesafe, any interface with raw Javascript isn't.
It's the same problem with Kotlin and Java integration, your Kotlin null checker doesn't matter if you receive data from Java which could be null (even if it's tagged as @NotNull).
That MR requires quite a time investment to evaluate it, also it depends on other MR.
I noticed it proposes run-time checks insertion. For a language like Object Pascal such run-time checks must be defined on a Language Theory level, otherwise compiler implementers/maintainers will have big problems reasoning about compiler's code and requirements over time.
Yes the compiletime checks as part of the DFA (Data Flow Analysis, when compiling with -Oodfa) would be the next step. This MR is just the first step, as in the current implementation FPC does not really map variant parts to the selector fields and things like this are possible:
TTest = record
case b: Boolean of
False: (A: Integer);
False: (B: Double);
end;
Also currently variant parts cannot have managed types, and as String is probably the most commonly used type in Pascal, not being able to have string usable would be kinda bad.
So this MR tries to do at first that (well and also requires the other MR by me to do the groundwork in the compiler to make this possible to begin with).
Once those are implemented the next step would be to do selector domain tracking in the DFA, basically trying to map which values the selector can have at which point in the code. With this static compiletime checks can be done like:
n:=GetPotenitalNull;
WriteLn(n.Value); // Error selector might be False
if n.HasValue then
WriteLn(n.Value); // no error because selector is guaranteed to be True
Note this would also allow the following:
type
TUnion<T, U> = record
case Selector: (First, Second) of
First: (First: T);
Second: (Second: U);
end;
var
u: TUnion<String, Integer>;
begin
u:=GetStringOrInt;
WriteLn(u.First); // Error selector may be second
if u.Selector = First then
WriteLn(u.First) // No error because selector selects the correct branch
else
WriteLn(u.Second); // No error because in else only the other branch can be selected
end;
Having a conservative check like the above wouldn't be too difficult to implement, basically just track a set of possible values (e.g. based on a range set) and make a set difference with the set of ranges that select a certain branch. If the difference is non empty it is not provable that the branch you are trying to access is actually selected and the compiler can throw an error.
This of course only works easily for simple comparison operators and maybe some basic arithmetic. Anything more complicated requires basically an SMT solver like Z3 (which is also why TypeScript has such an advanced typechecker, because Microsoft has a lot of in-house knowledge from their Z3 and formal analysis development teams)