Skip to content

Treat void-typed properties as optional #40823

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

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 24 additions & 4 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1352,6 +1352,18 @@ namespace ts {
}
}

/**
* Attempts to return the `SymbolLinks` for `symbol`, if one already exists.
* Does not allocate a symbol id or a `SymbolLinks` if it doesn't already exists.
*/
function tryGetSymbolLinks(symbol: Symbol): SymbolLinks | undefined {
if (symbol.flags & SymbolFlags.Transient) return <TransientSymbol>symbol;
return symbol.id ? symbolLinks[symbol.id] : undefined;
}

/**
* Gets or creates a `SymbolLinks` for `symbol`.
*/
function getSymbolLinks(symbol: Symbol): SymbolLinks {
if (symbol.flags & SymbolFlags.Transient) return <TransientSymbol>symbol;
const id = getSymbolId(symbol);
Expand Down Expand Up @@ -7666,11 +7678,11 @@ namespace ts {
function hasType(target: TypeSystemEntity, propertyName: TypeSystemPropertyName): boolean {
switch (propertyName) {
case TypeSystemPropertyName.Type:
return !!getSymbolLinks(<Symbol>target).type;
return !!tryGetSymbolLinks(<Symbol>target)?.type;
case TypeSystemPropertyName.EnumTagType:
return !!(getNodeLinks(target as JSDocEnumTag).resolvedEnumType);
case TypeSystemPropertyName.DeclaredType:
return !!getSymbolLinks(<Symbol>target).declaredType;
return !!tryGetSymbolLinks(<Symbol>target)?.declaredType;
case TypeSystemPropertyName.ResolvedBaseConstructorType:
return !!(<InterfaceType>target).resolvedBaseConstructorType;
case TypeSystemPropertyName.ResolvedReturnType:
Expand Down Expand Up @@ -8509,6 +8521,14 @@ namespace ts {
return links.type;
}

function isOptionalProperty(prop: Symbol) {
if (prop.flags & SymbolFlags.Optional) return true;
// We don't use `getTypeOfSymbol` here as we may be in the middle of resolving the type for `prop` and
// we shouldn't re-enter to check for `void`.
const type = tryGetSymbolLinks(prop)?.type;
return !!(type && forEachType(type, acceptsVoid));
}

function getTypeOfVariableOrParameterOrPropertyWorker(symbol: Symbol) {
// Handle prototype property
if (symbol.flags & SymbolFlags.Prototype) {
Expand Down Expand Up @@ -17894,7 +17914,7 @@ namespace ts {
return Ternary.False;
}
// When checking for comparability, be more lenient with optional properties.
if (!skipOptional && sourceProp.flags & SymbolFlags.Optional && !(targetProp.flags & SymbolFlags.Optional)) {
if (!skipOptional && sourceProp.flags & SymbolFlags.Optional && !isOptionalProperty(targetProp)) {
// TypeScript 1.0 spec (April 2014): 3.8.3
// S is a subtype of a type T, and T is a supertype of S if ...
// S' and T are object types and, for each member M in T..
Expand Down Expand Up @@ -19709,7 +19729,7 @@ namespace ts {
if (isStaticPrivateIdentifierProperty(targetProp)) {
continue;
}
if (requireOptionalProperties || !(targetProp.flags & SymbolFlags.Optional || getCheckFlags(targetProp) & CheckFlags.Partial)) {
if (requireOptionalProperties || !((getCheckFlags(targetProp) & CheckFlags.Partial) || isOptionalProperty(targetProp))) {
const sourceProp = getPropertyOfType(source, targetProp.escapedName);
if (!sourceProp) {
yield targetProp;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ tests/cases/conformance/jsx/inline/index.tsx(24,48): error TS2322: Type 'import(
[e: string]: {};
}
interface Element {
__domBrand: void;
__domBrand: never;
props: {
children?: Element[];
};
Expand All @@ -42,7 +42,7 @@ tests/cases/conformance/jsx/inline/index.tsx(24,48): error TS2322: Type 'import(
[e: string]: {};
}
interface Element {
__predomBrand: void;
__predomBrand: never;
props: {
children?: Element[];
};
Expand All @@ -66,7 +66,7 @@ tests/cases/conformance/jsx/inline/index.tsx(24,48): error TS2322: Type 'import(
!!! error TS2532: Object is possibly 'undefined'.

export class MyClass implements predom.JSX.Element {
__predomBrand!: void;
__predomBrand!: never;
constructor(public props: {x: number, y: number, children?: predom.JSX.Element[]}) {}
render() {
return <p>
Expand All @@ -92,7 +92,7 @@ tests/cases/conformance/jsx/inline/index.tsx(24,48): error TS2322: Type 'import(
const DOMSFC = (props: {x: number, y: number, children?: dom.JSX.Element[]}) => <p>{props.x} + {props.y} = {props.x + props.y}{props.children}</p>;

class DOMClass implements dom.JSX.Element {
__domBrand!: void;
__domBrand!: never;
constructor(public props: {x: number, y: number, children?: dom.JSX.Element[]}) {}
render() {
return <p>{this.props.x} + {this.props.y} = {this.props.x + this.props.y}{...this.props.children}</p>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export namespace dom {
[e: string]: {};
}
interface Element {
__domBrand: void;
__domBrand: never;
props: {
children?: Element[];
};
Expand All @@ -27,7 +27,7 @@ export namespace predom {
[e: string]: {};
}
interface Element {
__predomBrand: void;
__predomBrand: never;
props: {
children?: Element[];
};
Expand All @@ -47,7 +47,7 @@ import { predom } from "./renderer2"
export const MySFC = (props: {x: number, y: number, children?: predom.JSX.Element[]}) => <p>{props.x} + {props.y} = {props.x + props.y}{...this.props.children}</p>;

export class MyClass implements predom.JSX.Element {
__predomBrand!: void;
__predomBrand!: never;
constructor(public props: {x: number, y: number, children?: predom.JSX.Element[]}) {}
render() {
return <p>
Expand All @@ -70,7 +70,7 @@ elem = <h></h>; // Expect assignability error here
const DOMSFC = (props: {x: number, y: number, children?: dom.JSX.Element[]}) => <p>{props.x} + {props.y} = {props.x + props.y}{props.children}</p>;

class DOMClass implements dom.JSX.Element {
__domBrand!: void;
__domBrand!: never;
constructor(public props: {x: number, y: number, children?: dom.JSX.Element[]}) {}
render() {
return <p>{this.props.x} + {this.props.y} = {this.props.x + this.props.y}{...this.props.children}</p>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ export namespace dom {
interface Element {
>Element : Symbol(Element, Decl(renderer.d.ts, 4, 9))

__domBrand: void;
__domBrand: never;
>__domBrand : Symbol(Element.__domBrand, Decl(renderer.d.ts, 5, 27))

props: {
>props : Symbol(Element.props, Decl(renderer.d.ts, 6, 29))
>props : Symbol(Element.props, Decl(renderer.d.ts, 6, 30))

children?: Element[];
>children : Symbol(children, Decl(renderer.d.ts, 7, 20))
Expand Down Expand Up @@ -65,11 +65,11 @@ export namespace predom {
interface Element {
>Element : Symbol(Element, Decl(renderer2.d.ts, 4, 9))

__predomBrand: void;
__predomBrand: never;
>__predomBrand : Symbol(Element.__predomBrand, Decl(renderer2.d.ts, 5, 27))

props: {
>props : Symbol(Element.props, Decl(renderer2.d.ts, 6, 32))
>props : Symbol(Element.props, Decl(renderer2.d.ts, 6, 33))

children?: Element[];
>children : Symbol(children, Decl(renderer2.d.ts, 7, 20))
Expand Down Expand Up @@ -137,7 +137,7 @@ export class MyClass implements predom.JSX.Element {
>JSX : Symbol(predom.JSX, Decl(renderer2.d.ts, 0, 25))
>Element : Symbol(predom.JSX.Element, Decl(renderer2.d.ts, 4, 9))

__predomBrand!: void;
__predomBrand!: never;
>__predomBrand : Symbol(MyClass.__predomBrand, Decl(component.tsx, 5, 52))

constructor(public props: {x: number, y: number, children?: predom.JSX.Element[]}) {}
Expand Down Expand Up @@ -260,7 +260,7 @@ class DOMClass implements dom.JSX.Element {
>JSX : Symbol(dom.JSX, Decl(renderer.d.ts, 0, 22))
>Element : Symbol(dom.JSX.Element, Decl(renderer.d.ts, 4, 9))

__domBrand!: void;
__domBrand!: never;
>__domBrand : Symbol(DOMClass.__domBrand, Decl(index.tsx, 8, 43))

constructor(public props: {x: number, y: number, children?: dom.JSX.Element[]}) {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ export namespace dom {
>e : string
}
interface Element {
__domBrand: void;
>__domBrand : void
__domBrand: never;
>__domBrand : never

props: {
>props : { children?: Element[]; }
Expand Down Expand Up @@ -41,8 +41,8 @@ export namespace predom {
>e : string
}
interface Element {
__predomBrand: void;
>__predomBrand : void
__predomBrand: never;
>__predomBrand : never

props: {
>props : { children?: Element[]; }
Expand Down Expand Up @@ -110,8 +110,8 @@ export class MyClass implements predom.JSX.Element {
>predom : () => predom.JSX.Element
>JSX : any

__predomBrand!: void;
>__predomBrand : void
__predomBrand!: never;
>__predomBrand : never

constructor(public props: {x: number, y: number, children?: predom.JSX.Element[]}) {}
>props : { x: number; y: number; children?: predom.JSX.Element[]; }
Expand Down Expand Up @@ -246,8 +246,8 @@ class DOMClass implements dom.JSX.Element {
>dom : () => dom.JSX.Element
>JSX : any

__domBrand!: void;
>__domBrand : void
__domBrand!: never;
>__domBrand : never

constructor(public props: {x: number, y: number, children?: dom.JSX.Element[]}) {}
>props : { x: number; y: number; children?: dom.JSX.Element[]; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ tests/cases/conformance/jsx/inline/index.tsx(5,1): error TS2741: Property '__pre
[e: string]: {};
}
interface Element {
__domBrand: void;
__domBrand: never;
children: Element[];
props: {};
}
Expand All @@ -24,7 +24,7 @@ tests/cases/conformance/jsx/inline/index.tsx(5,1): error TS2741: Property '__pre
[e: string]: {};
}
interface Element {
__predomBrand: void;
__predomBrand: never;
children: Element[];
props: {};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ declare global {
[e: string]: {};
}
interface Element {
__domBrand: void;
__domBrand: never;
children: Element[];
props: {};
}
Expand All @@ -23,7 +23,7 @@ export namespace predom {
[e: string]: {};
}
interface Element {
__predomBrand: void;
__predomBrand: never;
children: Element[];
props: {};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ declare global {
interface Element {
>Element : Symbol(Element, Decl(renderer.d.ts, 4, 9))

__domBrand: void;
__domBrand: never;
>__domBrand : Symbol(Element.__domBrand, Decl(renderer.d.ts, 5, 27))

children: Element[];
>children : Symbol(Element.children, Decl(renderer.d.ts, 6, 29))
>children : Symbol(Element.children, Decl(renderer.d.ts, 6, 30))
>Element : Symbol(Element, Decl(renderer.d.ts, 4, 9))

props: {};
Expand Down Expand Up @@ -54,11 +54,11 @@ export namespace predom {
interface Element {
>Element : Symbol(Element, Decl(renderer2.d.ts, 4, 9))

__predomBrand: void;
__predomBrand: never;
>__predomBrand : Symbol(Element.__predomBrand, Decl(renderer2.d.ts, 5, 27))

children: Element[];
>children : Symbol(Element.children, Decl(renderer2.d.ts, 6, 32))
>children : Symbol(Element.children, Decl(renderer2.d.ts, 6, 33))
>Element : Symbol(Element, Decl(renderer2.d.ts, 4, 9))

props: {};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ declare global {
>e : string
}
interface Element {
__domBrand: void;
>__domBrand : void
__domBrand: never;
>__domBrand : never

children: Element[];
>children : Element[]
Expand All @@ -36,8 +36,8 @@ export namespace predom {
>e : string
}
interface Element {
__predomBrand: void;
>__predomBrand : void
__predomBrand: never;
>__predomBrand : never

children: Element[];
>children : Element[]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -264,9 +264,9 @@ tests/cases/compiler/strictFunctionTypesErrors.ts(155,5): error TS2322: Type '(c
i4 = i2; // Ok
i4 = i3; // Ok

interface Animal { animal: void }
interface Dog extends Animal { dog: void }
interface Cat extends Animal { cat: void }
interface Animal { animal: never }
interface Dog extends Animal { dog: never }
interface Cat extends Animal { cat: never }

interface Comparer1<T> {
compare(a: T, b: T): number;
Expand Down
6 changes: 3 additions & 3 deletions tests/baselines/reference/strictFunctionTypesErrors.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,9 @@ i4 = i1; // Ok
i4 = i2; // Ok
i4 = i3; // Ok

interface Animal { animal: void }
interface Dog extends Animal { dog: void }
interface Cat extends Animal { cat: void }
interface Animal { animal: never }
interface Dog extends Animal { dog: never }
interface Cat extends Animal { cat: never }

interface Comparer1<T> {
compare(a: T, b: T): number;
Expand Down
Loading