You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Infer bottom type for type argument when passing empty array #8878
any is sound here, but the inference should ideally find the most specific type that is sound. any is the least specific type (top type). never is more specific than every other type (bottom type). In the body of these functions, there will never be a value with the type of the type argument, so never is a sound type for the type argument.
never is very useful when used with union, as it then just disappears. Taking a union with any resolves in any and quickly everything is any. In this example, z is currently inferred as any but with this change, it would be undefined ( = never | undefined).
I thought the value of the generic parameter is derived from the type of the expression, so the fact that [] is any[] is used to refine T to any. Even if this isn't actually how the typechecker/binder actually works, it's a simple mental model for TS users. It would be confusing for [] to be inferred as any[] when assigning to a variable and never[] when assigning to a function parameter.
I thought the value of the generic parameter is derived from the type of the expression, so the fact that [] is any[] is used to refine T to any.
In general, that's true. Though with contextual typing, these types of the arguments will be inferred differently. For instance, the type of [1, ""] is (number | string)[]. But with contextual typing this could be inferred as a tuple type, [number, string]. For instance when foo<T, V>(a: [T, V]) is called with foo([1, ""]). I think that contextual typing could be used to infer the type of [] to never[].
think that contextual typing could be used to infer the type of [] to never[].
It could, but I can't see that being the intent in most cases. Almost every contextual type is as wide as can be inferred. You are suggesting this one of case (because you have a further widening challenge) to totally narrow down this one case to nothing. So far every use of never is when it is clear that all other types have been eliminated and the type can be no-longer narrowed. That would never be the case with an [] unless of core is it a frozen empty array.
The type never means that that code is not reachable. Currently that happens for instance with type guards (typeof x === "string" && typeof x === "number") and with a function that always throws (function a() { throw new Error(); }). When you pass [] to a function, and that argument is the only reference to a type argument, the body of that function can never have a value of that type. Thus that type is 'unreachable' in the body and never is thus valid here.
I also found out that the following code doesn't work as expected. y is inferred as (string | undefined)[], whereas it should be string[]. I think that this is related to this issue as well.
It should be string[]. The issue only shows up with --strictNullChecks and has to do with the fact that we still infer undefined[] for an empty array literal. We need to infer a true bottom type, so in strict null checking mode we should infer never[].
Now, regarding the original proposal to infer never[] for this example:
functiona<T>(xs: T[]){returnxs;}constx=a([]);// Could we infer never[]?
We actually do infer never[] for the array literal, but then we widen during type argument inference in the call a([]) so we end up with any[]. There was a proposal not to do this in #1436. The core issue is that we need to modify the widening algorithm to handle circularities and that is not trivial to do. See my comment here.
However, even if we stopped widening in type inference, it isn't clear that we shouldn't widen when we're inferring a type for x above. It would be reasonable to keep the never[] type only in situations where the type is deeply immutable (a concept we don't currently have), but a const is only shallowly immutable. As Arnav Singh (@Arnavion) points out, it would be odd if the following didn't work:
consta=[];a.push(5);// Don't want an error here
Contextual typing was also mentioned in the thread above, but that really doesn't have anything to do with widening.
Activity
kitsonk commentedon May 29, 2016
Wouldn't
anybe better, with an error ifnoImplicitAnyis used?There was also a lot of discussion of why
{}gets inferred for generics. Any change here would cause all sorts of carnage.ivogabe commentedon May 29, 2016
anyis sound here, but the inference should ideally find the most specific type that is sound.anyis the least specific type (top type).neveris more specific than every other type (bottom type). In the body of these functions, there will never be a value with the type of the type argument, soneveris a sound type for the type argument.neveris very useful when used with union, as it then just disappears. Taking a union withanyresolves inanyand quickly everything isany. In this example,zis currently inferred asanybut with this change, it would beundefined( =never | undefined).Arnavion commentedon May 29, 2016
needs to continue to work, so
xneeds to continue to beany[]in this case.ivogabe commentedon May 29, 2016
Arnav Singh (@Arnavion) Agree, but I only wanted this for values of a parameter with a type argument.
Arnavion commentedon May 29, 2016
I thought the value of the generic parameter is derived from the type of the expression, so the fact that
[]isany[]is used to refineTtoany. Even if this isn't actually how the typechecker/binder actually works, it's a simple mental model for TS users. It would be confusing for[]to be inferred asany[]when assigning to a variable andnever[]when assigning to a function parameter.ivogabe commentedon May 29, 2016
In general, that's true. Though with contextual typing, these types of the arguments will be inferred differently. For instance, the type of
[1, ""]is(number | string)[]. But with contextual typing this could be inferred as a tuple type,[number, string]. For instance whenfoo<T, V>(a: [T, V])is called withfoo([1, ""]). I think that contextual typing could be used to infer the type of[]tonever[].kitsonk commentedon May 30, 2016
It could, but I can't see that being the intent in most cases. Almost every contextual type is as wide as can be inferred. You are suggesting this one of case (because you have a further widening challenge) to totally narrow down this one case to nothing. So far every use of
neveris when it is clear that all other types have been eliminated and the type can be no-longer narrowed. That would never be the case with an[]unless of core is it a frozen empty array.ivogabe commentedon May 30, 2016
The type
nevermeans that that code is not reachable. Currently that happens for instance with type guards (typeof x === "string" && typeof x === "number") and with a function that always throws (function a() { throw new Error(); }). When you pass[]to a function, and that argument is the only reference to a type argument, the body of that function can never have a value of that type. Thus that type is 'unreachable' in the body andneveris thus valid here.I also found out that the following code doesn't work as expected.
yis inferred as(string | undefined)[], whereas it should bestring[]. I think that this is related to this issue as well.ahejlsberg commentedon May 30, 2016
Starting with your last example, it is definitely a bug that
yis inferred as(string | undefined)[]in this example:It should be
string[]. The issue only shows up with--strictNullChecksand has to do with the fact that we still inferundefined[]for an empty array literal. We need to infer a true bottom type, so in strict null checking mode we should infernever[].Now, regarding the original proposal to infer
never[]for this example:We actually do infer
never[]for the array literal, but then we widen during type argument inference in the calla([])so we end up withany[]. There was a proposal not to do this in #1436. The core issue is that we need to modify the widening algorithm to handle circularities and that is not trivial to do. See my comment here.However, even if we stopped widening in type inference, it isn't clear that we shouldn't widen when we're inferring a type for
xabove. It would be reasonable to keep thenever[]type only in situations where the type is deeply immutable (a concept we don't currently have), but aconstis only shallowly immutable. As Arnav Singh (@Arnavion) points out, it would be odd if the following didn't work:Contextual typing was also mentioned in the thread above, but that really doesn't have anything to do with widening.
ahejlsberg commentedon May 30, 2016
I did some edits to the comment above to fix some inaccuracies. It should be good now.