Skip to content

Commit 734e9de

Browse files
authored
Use union types rather than base classes for Sass nodes (#2537)
1 parent 0d3ea25 commit 734e9de

33 files changed

+317
-283
lines changed

pkg/sass-parser/CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
## 0.4.16-dev
22

3+
* Use union types rather than base classes for Sass nodes wherever possible.
4+
This makes it possible for TypeScript to automatically narrow node types based
5+
on `sassType` checks.
6+
37
* Add support for parsing null literals.
48

59
* Add support for parsing parenthesized expressions.

pkg/sass-parser/lib/src/argument.ts

+13-13
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import * as postcss from 'postcss';
66

7-
import {Expression, ExpressionProps} from './expression';
7+
import {AnyExpression, ExpressionProps} from './expression';
88
import {fromProps} from './expression/from-props';
99
import {Node, NodeProps} from './node';
1010
import {ArgumentList} from './argument-list';
@@ -60,7 +60,7 @@ export interface ArgumentRaws {
6060
*/
6161
export type ArgumentObjectProps = NodeProps & {
6262
raws?: ArgumentRaws;
63-
value: Expression | ExpressionProps;
63+
value: AnyExpression | ExpressionProps;
6464
} & ({name?: string; rest?: never} | {name?: never; rest?: boolean});
6565

6666
/**
@@ -70,7 +70,7 @@ export type ArgumentObjectProps = NodeProps & {
7070
* ArgumentDeclarationProps}.
7171
*/
7272
export type ArgumentExpressionProps =
73-
| Expression
73+
| AnyExpression
7474
| ExpressionProps
7575
| Omit<ArgumentObjectProps, 'name'>;
7676

@@ -81,7 +81,7 @@ export type ArgumentExpressionProps =
8181
*/
8282
export type ArgumentProps =
8383
| ArgumentObjectProps
84-
| Expression
84+
| AnyExpression
8585
| ExpressionProps
8686
| [string, ArgumentExpressionProps];
8787

@@ -115,16 +115,16 @@ export class Argument extends Node {
115115
private declare _name?: string;
116116

117117
/** The argument's value. */
118-
get value(): Expression {
118+
get value(): AnyExpression {
119119
return this._value!;
120120
}
121-
set value(value: Expression | ExpressionProps) {
121+
set value(value: AnyExpression | ExpressionProps) {
122122
if (this._value) this._value.parent = undefined;
123-
if (!('sassType' in value)) value = fromProps(value);
124-
if (value) value.parent = this;
125-
this._value = value;
123+
const built = 'sassType' in value ? value : fromProps(value);
124+
built.parent = this;
125+
this._value = built;
126126
}
127-
private declare _value?: Expression;
127+
private declare _value?: AnyExpression;
128128

129129
/**
130130
* Whether this is a rest argument (indicated by `...` in Sass).
@@ -147,14 +147,14 @@ export class Argument extends Node {
147147
if ('sassType' in props || !('value' in props)) {
148148
defaults = {
149149
name,
150-
value: props as Expression | ExpressionProps,
150+
value: props as AnyExpression | ExpressionProps,
151151
};
152152
} else {
153153
defaults = {name, ...props} as ArgumentObjectProps;
154154
}
155155
} else if ('sassType' in defaults || !('value' in defaults)) {
156156
defaults = {
157-
value: defaults as Expression | ExpressionProps,
157+
value: defaults as AnyExpression | ExpressionProps,
158158
};
159159
}
160160
super(defaults);
@@ -193,7 +193,7 @@ export class Argument extends Node {
193193
}
194194

195195
/** @hidden */
196-
get nonStatementChildren(): ReadonlyArray<Expression> {
196+
get nonStatementChildren(): ReadonlyArray<AnyExpression> {
197197
return [this.value];
198198
}
199199
}

pkg/sass-parser/lib/src/configured-variable.ts

+11-11
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import * as postcss from 'postcss';
66

77
import {Configuration} from './configuration';
88
import {convertExpression} from './expression/convert';
9-
import {Expression, ExpressionProps} from './expression';
9+
import {AnyExpression, ExpressionProps} from './expression';
1010
import {fromProps} from './expression/from-props';
1111
import {LazySource} from './lazy-source';
1212
import {Node, NodeProps} from './node';
@@ -56,7 +56,7 @@ export interface ConfiguredVariableRaws {
5656
export interface ConfiguredVariableObjectProps extends NodeProps {
5757
raws?: ConfiguredVariableRaws;
5858
name: string;
59-
expression: Expression | ExpressionProps;
59+
expression: AnyExpression | ExpressionProps;
6060
guarded?: boolean;
6161
}
6262

@@ -70,7 +70,7 @@ export interface ConfiguredVariableObjectProps extends NodeProps {
7070
* creates an unguarded {@link ConfiguredVariable}.
7171
*/
7272
export type ConfiguredVariableExpressionProps =
73-
| Expression
73+
| AnyExpression
7474
| ExpressionProps
7575
| Omit<ConfiguredVariableObjectProps, 'name'>;
7676

@@ -103,16 +103,16 @@ export class ConfiguredVariable extends Node {
103103
declare name: string;
104104

105105
/** The expression whose value the variable is assigned. */
106-
get expression(): Expression {
106+
get expression(): AnyExpression {
107107
return this._expression!;
108108
}
109-
set expression(value: Expression | ExpressionProps) {
109+
set expression(value: AnyExpression | ExpressionProps) {
110110
if (this._expression) this._expression.parent = undefined;
111-
if (!('sassType' in value)) value = fromProps(value);
112-
if (value) value.parent = this;
113-
this._expression = value;
111+
const built = 'sassType' in value ? value : fromProps(value);
112+
built.parent = this;
113+
this._expression = built;
114114
}
115-
private declare _expression: Expression;
115+
private declare _expression: AnyExpression;
116116

117117
/** Whether this has a `!default` guard. */
118118
declare guarded: boolean;
@@ -129,7 +129,7 @@ export class ConfiguredVariable extends Node {
129129
if ('sassType' in rest || !('expression' in rest)) {
130130
defaults = {
131131
name,
132-
expression: rest as Expression | ExpressionProps,
132+
expression: rest as AnyExpression | ExpressionProps,
133133
};
134134
} else {
135135
defaults = {name, ...rest};
@@ -178,7 +178,7 @@ export class ConfiguredVariable extends Node {
178178
}
179179

180180
/** @hidden */
181-
get nonStatementChildren(): ReadonlyArray<Expression> {
181+
get nonStatementChildren(): ReadonlyArray<AnyExpression> {
182182
return [this.expression];
183183
}
184184
}

pkg/sass-parser/lib/src/dynamic-import.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ import * as postcss from 'postcss';
77
import {StringExpression} from './expression/string';
88
import {ImportList} from './import-list';
99
import {LazySource} from './lazy-source';
10-
import {Node, NodeProps} from './node';
10+
import {AnyNode, Node, NodeProps} from './node';
1111
import * as sassInternal from './sass-internal';
1212
import {RawWithValue} from './raw-with-value';
13-
import {Statement} from './statement';
13+
import {AnyStatement} from './statement';
1414
import * as utils from './utils';
1515

1616
/**
@@ -103,7 +103,7 @@ export class DynamicImport extends Node {
103103
}
104104

105105
/** @hidden */
106-
get nonStatementChildren(): ReadonlyArray<Exclude<Node, Statement>> {
106+
get nonStatementChildren(): ReadonlyArray<Exclude<AnyNode, AnyStatement>> {
107107
return [];
108108
}
109109
}

pkg/sass-parser/lib/src/expression/binary-operation.ts

+16-16
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {LazySource} from '../lazy-source';
88
import {NodeProps} from '../node';
99
import type * as sassInternal from '../sass-internal';
1010
import * as utils from '../utils';
11-
import {Expression, ExpressionProps} from '.';
11+
import {AnyExpression, Expression, ExpressionProps} from '.';
1212
import {convertExpression} from './convert';
1313
import {fromProps} from './from-props';
1414

@@ -36,8 +36,8 @@ export type BinaryOperator =
3636
*/
3737
export interface BinaryOperationExpressionProps extends NodeProps {
3838
operator: BinaryOperator;
39-
left: Expression | ExpressionProps;
40-
right: Expression | ExpressionProps;
39+
left: AnyExpression | ExpressionProps;
40+
right: AnyExpression | ExpressionProps;
4141
raws?: BinaryOperationExpressionRaws;
4242
}
4343

@@ -80,30 +80,30 @@ export class BinaryOperationExpression extends Expression {
8080
private declare _operator: BinaryOperator;
8181

8282
/** The expression on the left-hand side of this operation. */
83-
get left(): Expression {
83+
get left(): AnyExpression {
8484
return this._left;
8585
}
86-
set left(left: Expression | ExpressionProps) {
86+
set left(left: AnyExpression | ExpressionProps) {
8787
// TODO - postcss/postcss#1957: Mark this as dirty
8888
if (this._left) this._left.parent = undefined;
89-
if (!('sassType' in left)) left = fromProps(left);
90-
left.parent = this;
91-
this._left = left;
89+
const built = 'sassType' in left ? left : fromProps(left);
90+
built.parent = this;
91+
this._left = built;
9292
}
93-
private declare _left: Expression;
93+
private declare _left: AnyExpression;
9494

9595
/** The expression on the right-hand side of this operation. */
96-
get right(): Expression {
96+
get right(): AnyExpression {
9797
return this._right;
9898
}
99-
set right(right: Expression | ExpressionProps) {
99+
set right(right: AnyExpression | ExpressionProps) {
100100
// TODO - postcss/postcss#1957: Mark this as dirty
101101
if (this._right) this._right.parent = undefined;
102-
if (!('sassType' in right)) right = fromProps(right);
103-
right.parent = this;
104-
this._right = right;
102+
const built = 'sassType' in right ? right : fromProps(right);
103+
built.parent = this;
104+
this._right = built;
105105
}
106-
private declare _right: Expression;
106+
private declare _right: AnyExpression;
107107

108108
constructor(defaults: BinaryOperationExpressionProps);
109109
/** @hidden */
@@ -146,7 +146,7 @@ export class BinaryOperationExpression extends Expression {
146146
}
147147

148148
/** @hidden */
149-
get nonStatementChildren(): ReadonlyArray<Expression> {
149+
get nonStatementChildren(): ReadonlyArray<AnyExpression> {
150150
return [this.left, this.right];
151151
}
152152
}

pkg/sass-parser/lib/src/expression/boolean.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ export class BooleanExpression extends Expression {
7777
}
7878

7979
/** @hidden */
80-
get nonStatementChildren(): ReadonlyArray<Expression> {
80+
get nonStatementChildren(): ReadonlyArray<never> {
8181
return [];
8282
}
8383
}

pkg/sass-parser/lib/src/expression/color.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ export class ColorExpression extends Expression {
115115
}
116116

117117
/** @hidden */
118-
get nonStatementChildren(): ReadonlyArray<Expression> {
118+
get nonStatementChildren(): ReadonlyArray<never> {
119119
return [];
120120
}
121121
}

pkg/sass-parser/lib/src/expression/convert.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import * as sassInternal from '../sass-internal';
77
import {ArgumentList} from '../argument-list';
88
import {Interpolation} from '../interpolation';
99
import {LazySource} from '../lazy-source';
10-
import {Expression} from '.';
10+
import {AnyExpression} from '.';
1111
import {BinaryOperationExpression} from './binary-operation';
1212
import {BooleanExpression} from './boolean';
1313
import {ColorExpression} from './color';
@@ -22,7 +22,7 @@ import {SelectorExpression} from './selector';
2222
import {StringExpression} from './string';
2323

2424
/** The visitor to use to convert internal Sass nodes to JS. */
25-
const visitor = sassInternal.createExpressionVisitor<Expression>({
25+
const visitor = sassInternal.createExpressionVisitor<AnyExpression>({
2626
visitBinaryOperationExpression: inner =>
2727
new BinaryOperationExpression(undefined, inner),
2828
visitBooleanExpression: inner => new BooleanExpression(undefined, inner),
@@ -57,6 +57,6 @@ const visitor = sassInternal.createExpressionVisitor<Expression>({
5757
/** Converts an internal expression AST node into an external one. */
5858
export function convertExpression(
5959
expression: sassInternal.Expression,
60-
): Expression {
60+
): AnyExpression {
6161
return expression.accept(visitor);
6262
}

pkg/sass-parser/lib/src/expression/from-props.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// https://opensource.org/licenses/MIT.
44

55
import * as sass from 'sass';
6-
import {Expression, ExpressionProps} from '.';
6+
import {AnyExpression, ExpressionProps} from '.';
77
import {BinaryOperationExpression} from './binary-operation';
88
import {BooleanExpression} from './boolean';
99
import {ColorExpression} from './color';
@@ -17,7 +17,7 @@ import {ParenthesizedExpression} from './parenthesized';
1717
import {StringExpression} from './string';
1818

1919
/** Constructs an expression from {@link ExpressionProps}. */
20-
export function fromProps(props: ExpressionProps): Expression {
20+
export function fromProps(props: ExpressionProps): AnyExpression {
2121
if ('text' in props) return new StringExpression(props);
2222
if ('left' in props) return new BinaryOperationExpression(props);
2323
if ('separator' in props) return new ListExpression(props);

pkg/sass-parser/lib/src/expression/interpolated-function.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import * as postcss from 'postcss';
77
import {ArgumentList, ArgumentListProps} from '../argument-list';
88
import {Interpolation, InterpolationProps} from '../interpolation';
99
import {LazySource} from '../lazy-source';
10-
import {Node, NodeProps} from '../node';
10+
import {AnyNode, NodeProps} from '../node';
11+
import {AnyStatement} from '../statement';
1112
import type * as sassInternal from '../sass-internal';
1213
import * as utils from '../utils';
1314
import {Expression} from '.';
@@ -102,7 +103,7 @@ export class InterpolatedFunctionExpression extends Expression {
102103
}
103104

104105
/** @hidden */
105-
get nonStatementChildren(): ReadonlyArray<Node> {
106+
get nonStatementChildren(): ReadonlyArray<Exclude<AnyNode, AnyStatement>> {
106107
return [this.name, this.arguments];
107108
}
108109
}

0 commit comments

Comments
 (0)