@mapbox/mapbox-gl-style-spec 14.28.0 → 14.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/composite.ts +5 -5
  2. package/diff.ts +38 -40
  3. package/dist/index.cjs +362 -238
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.d.ts +101 -13
  6. package/dist/index.es.js +362 -238
  7. package/dist/index.es.js.map +1 -1
  8. package/expression/compound_expression.ts +6 -6
  9. package/expression/definitions/assertion.ts +9 -12
  10. package/expression/definitions/at.ts +1 -1
  11. package/expression/definitions/at_interpolated.ts +1 -1
  12. package/expression/definitions/case.ts +1 -2
  13. package/expression/definitions/coalesce.ts +1 -1
  14. package/expression/definitions/coercion.ts +10 -12
  15. package/expression/definitions/collator.ts +8 -8
  16. package/expression/definitions/comparison.ts +0 -1
  17. package/expression/definitions/distance.ts +135 -73
  18. package/expression/definitions/format.ts +10 -13
  19. package/expression/definitions/image.ts +8 -8
  20. package/expression/definitions/index.ts +92 -90
  21. package/expression/definitions/interpolate.ts +18 -19
  22. package/expression/definitions/let.ts +1 -2
  23. package/expression/definitions/literal.ts +1 -1
  24. package/expression/definitions/match.ts +7 -8
  25. package/expression/definitions/number_format.ts +7 -8
  26. package/expression/definitions/step.ts +11 -11
  27. package/expression/definitions/within.ts +23 -24
  28. package/expression/evaluation_context.ts +4 -4
  29. package/expression/expression.ts +1 -1
  30. package/expression/index.ts +43 -21
  31. package/expression/parsing_context.ts +2 -2
  32. package/expression/stops.ts +2 -2
  33. package/expression/values.ts +2 -2
  34. package/feature_filter/index.ts +3 -3
  35. package/function/convert.ts +8 -8
  36. package/function/index.ts +4 -2
  37. package/group_by_layout.ts +6 -4
  38. package/package.json +1 -1
  39. package/read_style.ts +2 -2
  40. package/reference/v8.json +86 -5
  41. package/types/config_options.ts +1 -1
  42. package/types.ts +16 -6
  43. package/util/properties.ts +1 -1
  44. package/validate/validate.ts +15 -5
  45. package/validate/validate_appearance.ts +7 -7
  46. package/validate/validate_array.ts +7 -7
  47. package/validate/validate_enum.ts +2 -3
  48. package/validate/validate_fog.ts +2 -2
  49. package/validate/validate_function.ts +14 -13
  50. package/validate/validate_layer.ts +10 -9
  51. package/validate/validate_light.ts +2 -2
  52. package/validate/validate_lights.ts +3 -3
  53. package/validate/validate_number.ts +10 -7
  54. package/validate/validate_object.ts +11 -10
  55. package/validate/validate_option.ts +24 -9
  56. package/validate/validate_property.ts +4 -4
  57. package/validate/validate_rain.ts +1 -1
  58. package/validate/validate_snow.ts +1 -1
  59. package/validate/validate_style.ts +2 -1
  60. package/validate/validate_terrain.ts +2 -2
  61. package/validate_mapbox_api_supported.ts +5 -6
  62. package/validate_style.min.ts +2 -1
  63. package/visit.ts +11 -10
@@ -111,7 +111,7 @@ class Interpolate implements Expression {
111
111
 
112
112
  const stops: Stops = [];
113
113
 
114
- let outputType: Type = null;
114
+ let outputType: Type | null = null;
115
115
  if (operator === 'interpolate-hcl' || operator === 'interpolate-lab') {
116
116
  outputType = ColorType;
117
117
  } else if (context.expectedType && context.expectedType.kind !== 'value') {
@@ -129,7 +129,7 @@ class Interpolate implements Expression {
129
129
  return context.error('Input/output pairs for "interpolate" expressions must be defined using literal numeric values (not computed expressions) for the input values.', labelKey);
130
130
  }
131
131
 
132
- if (stops.length && stops.at(-1)[0] >= label) {
132
+ if (stops.length && stops.at(-1)![0] >= label) {
133
133
  return context.error('Input/output pairs for "interpolate" expressions must be arranged with input values in strictly ascending order.', labelKey);
134
134
  }
135
135
 
@@ -139,15 +139,15 @@ class Interpolate implements Expression {
139
139
  stops.push([label, parsed]);
140
140
  }
141
141
 
142
- if (outputType.kind !== 'number' &&
143
- outputType.kind !== 'color' &&
142
+ if (outputType!.kind !== 'number' &&
143
+ outputType!.kind !== 'color' &&
144
144
  !(
145
- outputType.kind === 'array' &&
145
+ outputType!.kind === 'array' &&
146
146
  outputType.itemType.kind === 'number' &&
147
147
  typeof outputType.N === 'number'
148
148
  )
149
149
  ) {
150
- return context.error(`Type ${toString(outputType)} is not interpolatable.`);
150
+ return context.error(`Type ${toString(outputType!)} is not interpolatable.`);
151
151
  }
152
152
 
153
153
  return new Interpolate(outputType, operator as InterpolationOperator, interpolation as InterpolationType, input as Expression, stops);
@@ -158,33 +158,32 @@ class Interpolate implements Expression {
158
158
  const outputs = this.outputs;
159
159
 
160
160
  if (labels.length === 1) {
161
- return outputs[0].evaluate(ctx) as Color;
161
+ return outputs[0]!.evaluate(ctx) as Color;
162
162
  }
163
163
 
164
164
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
165
165
  const value: number = this.input.evaluate(ctx);
166
- if (value <= labels[0]) {
167
- return outputs[0].evaluate(ctx) as Color;
166
+ if (value <= labels[0]!) {
167
+ return outputs[0]!.evaluate(ctx) as Color;
168
168
  }
169
169
 
170
170
  const stopCount = labels.length;
171
- if (value >= labels[stopCount - 1]) {
172
- return outputs[stopCount - 1].evaluate(ctx) as Color;
171
+ if (value >= labels[stopCount - 1]!) {
172
+ return outputs[stopCount - 1]!.evaluate(ctx) as Color;
173
173
  }
174
174
 
175
175
  const index = findStopLessThanOrEqualTo(labels, value);
176
- const lower = labels[index];
177
- const upper = labels[index + 1];
176
+ const lower = labels[index]!;
177
+ const upper = labels[index + 1]!;
178
178
  const t = Interpolate.interpolationFactor(this.interpolation, value, lower, upper);
179
179
 
180
180
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
181
- const outputLower: Color = outputs[index].evaluate(ctx);
181
+ const outputLower: Color = outputs[index]!.evaluate(ctx);
182
182
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
183
- const outputUpper: Color = outputs[index + 1].evaluate(ctx);
183
+ const outputUpper: Color = outputs[index + 1]!.evaluate(ctx);
184
184
 
185
185
  if (this.operator === 'interpolate') {
186
- // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call
187
- return interpolate[this.type.kind.toLowerCase()](outputLower, outputUpper, t);
186
+ return (interpolate[this.type.kind.toLowerCase() as keyof typeof interpolate] as (from: Color, to: Color, t: number) => Color)(outputLower, outputUpper, t);
188
187
  } else if (this.operator === 'interpolate-hcl') {
189
188
  return hcl.reverse(hcl.interpolate(hcl.forward(outputLower), hcl.forward(outputUpper), t));
190
189
  } else {
@@ -221,8 +220,8 @@ class Interpolate implements Expression {
221
220
 
222
221
  for (let i = 0; i < this.labels.length; i++) {
223
222
  serialized.push(
224
- this.labels[i],
225
- this.outputs[i].serialize()
223
+ this.labels[i]!,
224
+ this.outputs[i]!.serialize()
226
225
  );
227
226
  }
228
227
  return serialized;
@@ -12,8 +12,7 @@ class Let implements Expression {
12
12
 
13
13
  constructor(bindings: Array<[string, Expression]>, result: Expression) {
14
14
  this.type = result.type;
15
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
16
- this.bindings = [].concat(bindings);
15
+ this.bindings = ([] as Array<[string, Expression]>).concat(bindings);
17
16
  this.result = result;
18
17
  }
19
18
 
@@ -67,7 +67,7 @@ class Literal implements Expression {
67
67
  typeof this.value === 'string' ||
68
68
  typeof this.value === 'number' ||
69
69
  typeof this.value === 'boolean');
70
- return this.value as SerializedExpression;
70
+ return this.value;
71
71
  }
72
72
  }
73
73
  }
@@ -94,14 +94,13 @@ class Match implements Expression {
94
94
  return null;
95
95
  }
96
96
 
97
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
98
97
  return new Match(inputType, outputType, input, cases, outputs, otherwise);
99
98
  }
100
99
 
101
100
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
102
101
  evaluate(ctx: EvaluationContext): any {
103
102
  const input = (this.input.evaluate(ctx)) as number | string;
104
- const output = (typeEquals(typeOf(input), this.inputType) && this.outputs[this.cases[input]]) || this.otherwise;
103
+ const output = (typeEquals(typeOf(input), this.inputType) && this.outputs[this.cases[input]!]) || this.otherwise;
105
104
  return output.evaluate(ctx);
106
105
  }
107
106
 
@@ -129,14 +128,14 @@ class Match implements Expression {
129
128
  [index: number]: number;
130
129
  } = {}; // lookup index into groupedByOutput for a given output expression
131
130
  for (const label of sortedLabels) {
132
- const outputIndex = outputLookup[this.cases[label]];
131
+ const outputIndex = outputLookup[this.cases[label]!];
133
132
  if (outputIndex === undefined) {
134
133
  // First time seeing this output, add it to the end of the grouped list
135
- outputLookup[this.cases[label]] = groupedByOutput.length;
136
- groupedByOutput.push([this.cases[label], [label]]);
134
+ outputLookup[this.cases[label]!] = groupedByOutput.length;
135
+ groupedByOutput.push([this.cases[label]!, [label]]);
137
136
  } else {
138
137
  // We've seen this expression before, add the label to that output's group
139
- groupedByOutput[outputIndex][1].push(label);
138
+ groupedByOutput[outputIndex]![1].push(label);
140
139
  }
141
140
  }
142
141
 
@@ -145,12 +144,12 @@ class Match implements Expression {
145
144
  for (const [outputIndex, labels] of groupedByOutput) {
146
145
  if (labels.length === 1) {
147
146
  // Only a single label matches this output expression
148
- serialized.push(coerceLabel(labels[0]));
147
+ serialized.push(coerceLabel(labels[0]!));
149
148
  } else {
150
149
  // Array of literal labels pointing to this output expression
151
150
  serialized.push(labels.map(coerceLabel));
152
151
  }
153
- serialized.push(this.outputs[outputIndex].serialize());
152
+ serialized.push(this.outputs[outputIndex]!.serialize());
154
153
  }
155
154
  serialized.push(this.otherwise.serialize());
156
155
  return serialized;
@@ -31,48 +31,47 @@ export default class NumberFormat implements Expression {
31
31
  this.maxFractionDigits = maxFractionDigits;
32
32
  }
33
33
 
34
- static parse(args: ReadonlyArray<unknown>, context: ParsingContext): Expression | void {
34
+ static parse(args: ReadonlyArray<unknown>, context: ParsingContext): Expression | null | void {
35
35
  if (args.length !== 3)
36
36
  return context.error(`Expected two arguments.`);
37
37
 
38
38
  const number = context.parse(args[1], 1, NumberType);
39
39
  if (!number) return null;
40
40
 
41
- const options = args[2];
41
+ const options = args[2] as Record<string, unknown>;
42
42
  if (typeof options !== "object" || Array.isArray(options))
43
43
  return context.error(`NumberFormat options argument must be an object.`);
44
44
 
45
- let locale = null;
45
+ let locale: Expression | null | void = null;
46
46
  if (options['locale']) {
47
47
  locale = context.parseObjectValue(options['locale'], 2, 'locale', StringType);
48
48
  if (!locale) return null;
49
49
  }
50
50
 
51
- let currency = null;
51
+ let currency: Expression | null | void = null;
52
52
  if (options['currency']) {
53
53
  currency = context.parseObjectValue(options['currency'], 2, 'currency', StringType);
54
54
  if (!currency) return null;
55
55
  }
56
56
 
57
- let unit = null;
57
+ let unit: Expression | null | void = null;
58
58
  if (options['unit']) {
59
59
  unit = context.parseObjectValue(options['unit'], 2, 'unit', StringType);
60
60
  if (!unit) return null;
61
61
  }
62
62
 
63
- let minFractionDigits = null;
63
+ let minFractionDigits: Expression | null | void = null;
64
64
  if (options['min-fraction-digits'] !== undefined) {
65
65
  minFractionDigits = context.parseObjectValue(options['min-fraction-digits'], 2, 'min-fraction-digits', NumberType);
66
66
  if (!minFractionDigits) return null;
67
67
  }
68
68
 
69
- let maxFractionDigits = null;
69
+ let maxFractionDigits: Expression | null | void = null;
70
70
  if (options['max-fraction-digits'] !== undefined) {
71
71
  maxFractionDigits = context.parseObjectValue(options['max-fraction-digits'], 2, 'max-fraction-digits', NumberType);
72
72
  if (!maxFractionDigits) return null;
73
73
  }
74
74
 
75
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
76
75
  return new NumberFormat(number, locale, currency, unit, minFractionDigits, maxFractionDigits);
77
76
  }
78
77
 
@@ -40,7 +40,7 @@ class Step implements Expression {
40
40
 
41
41
  const stops: Stops = [];
42
42
 
43
- let outputType: Type = null;
43
+ let outputType: Type | null = null;
44
44
  if (context.expectedType && context.expectedType.kind !== 'value') {
45
45
  outputType = context.expectedType;
46
46
  }
@@ -56,7 +56,7 @@ class Step implements Expression {
56
56
  return context.error('Input/output pairs for "step" expressions must be defined using literal numeric values (not computed expressions) for the input values.', labelKey);
57
57
  }
58
58
 
59
- if (stops.length && stops.at(-1)[0] >= label) {
59
+ if (stops.length && stops.at(-1)![0] >= label) {
60
60
  return context.error('Input/output pairs for "step" expressions must be arranged with input values in strictly ascending order.', labelKey);
61
61
  }
62
62
 
@@ -66,7 +66,7 @@ class Step implements Expression {
66
66
  stops.push([label, parsed]);
67
67
  }
68
68
 
69
- return new Step(outputType, input, stops);
69
+ return new Step(outputType!, input, stops);
70
70
  }
71
71
 
72
72
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -75,21 +75,21 @@ class Step implements Expression {
75
75
  const outputs = this.outputs;
76
76
 
77
77
  if (labels.length === 1) {
78
- return outputs[0].evaluate(ctx);
78
+ return outputs[0]!.evaluate(ctx);
79
79
  }
80
80
 
81
81
  const value = (this.input.evaluate(ctx) as number);
82
- if (value <= labels[0]) {
83
- return outputs[0].evaluate(ctx);
82
+ if (value <= labels[0]!) {
83
+ return outputs[0]!.evaluate(ctx);
84
84
  }
85
85
 
86
86
  const stopCount = labels.length;
87
- if (value >= labels[stopCount - 1]) {
88
- return outputs[stopCount - 1].evaluate(ctx);
87
+ if (value >= labels[stopCount - 1]!) {
88
+ return outputs[stopCount - 1]!.evaluate(ctx);
89
89
  }
90
90
 
91
91
  const index = findStopLessThanOrEqualTo(labels, value);
92
- return outputs[index].evaluate(ctx);
92
+ return outputs[index]!.evaluate(ctx);
93
93
  }
94
94
 
95
95
  eachChild(fn: (_: Expression) => void) {
@@ -107,9 +107,9 @@ class Step implements Expression {
107
107
  const serialized = ["step", this.input.serialize()];
108
108
  for (let i = 0; i < this.labels.length; i++) {
109
109
  if (i > 0) {
110
- serialized.push(this.labels[i]);
110
+ serialized.push(this.labels[i]!);
111
111
  }
112
- serialized.push(this.outputs[i].serialize());
112
+ serialized.push(this.outputs[i]!.serialize());
113
113
  }
114
114
  return serialized;
115
115
  }
@@ -15,16 +15,16 @@ type GeoJSONPolygons = GeoJSON.Polygon | GeoJSON.MultiPolygon;
15
15
 
16
16
  const EXTENT = 8192;
17
17
 
18
- function getTileCoordinates(p: GeoJSON.Position, canonical: CanonicalTileID) {
19
- const x = mercatorXfromLng(p[0]);
20
- const y = mercatorYfromLat(p[1]);
18
+ function getTileCoordinates(p: GeoJSON.Position, canonical: CanonicalTileID): [number, number] {
19
+ const x = mercatorXfromLng(p[0]!);
20
+ const y = mercatorYfromLat(p[1]!);
21
21
  const tilesAtZoom = Math.pow(2, canonical.z);
22
22
  return [Math.round(x * tilesAtZoom * EXTENT), Math.round(y * tilesAtZoom * EXTENT)];
23
23
  }
24
24
 
25
25
  function pointWithinPolygons(point: GeoJSON.Position, polygons: Array<Array<Array<GeoJSON.Position>>>) {
26
26
  for (let i = 0; i < polygons.length; i++) {
27
- if (pointWithinPolygon(point, polygons[i])) return true;
27
+ if (pointWithinPolygon(point, polygons[i]!)) return true;
28
28
  }
29
29
  return false;
30
30
  }
@@ -33,8 +33,8 @@ function lineIntersectPolygon(p1: GeoJSON.Position, p2: GeoJSON.Position, polygo
33
33
  for (const ring of polygon) {
34
34
  // loop through every edge of the ring
35
35
  for (let j = 0, len = ring.length, k = len - 1; j < len; k = j++) {
36
- const q1 = ring[k];
37
- const q2 = ring[j];
36
+ const q1 = ring[k]!;
37
+ const q2 = ring[j]!;
38
38
  if (segmentIntersectSegment(p1, p2, q1, q2)) {
39
39
  return true;
40
40
  }
@@ -46,14 +46,14 @@ function lineIntersectPolygon(p1: GeoJSON.Position, p2: GeoJSON.Position, polygo
46
46
  function lineStringWithinPolygon(line: Array<GeoJSON.Position>, polygon: Array<Array<GeoJSON.Position>>) {
47
47
  // First, check if geometry points of line segments are all inside polygon
48
48
  for (let i = 0; i < line.length; ++i) {
49
- if (!pointWithinPolygon(line[i], polygon)) {
49
+ if (!pointWithinPolygon(line[i]!, polygon)) {
50
50
  return false;
51
51
  }
52
52
  }
53
53
 
54
54
  // Second, check if there is line segment intersecting polygon edge
55
55
  for (let i = 0; i < line.length - 1; ++i) {
56
- if (lineIntersectPolygon(line[i], line[i + 1], polygon)) {
56
+ if (lineIntersectPolygon(line[i]!, line[i + 1]!, polygon)) {
57
57
  return false;
58
58
  }
59
59
  }
@@ -62,7 +62,7 @@ function lineStringWithinPolygon(line: Array<GeoJSON.Position>, polygon: Array<A
62
62
 
63
63
  function lineStringWithinPolygons(line: Array<GeoJSON.Position>, polygons: Array<Array<Array<GeoJSON.Position>>>) {
64
64
  for (let i = 0; i < polygons.length; i++) {
65
- if (lineStringWithinPolygon(line, polygons[i])) return true;
65
+ if (lineStringWithinPolygon(line, polygons[i]!)) return true;
66
66
  }
67
67
  return false;
68
68
  }
@@ -71,8 +71,8 @@ function getTilePolygon(coordinates: Array<Array<GeoJSON.Position>>, bbox: BBox,
71
71
  const polygon: Array<Array<number[]>> = [];
72
72
  for (let i = 0; i < coordinates.length; i++) {
73
73
  const ring: number[][] = [];
74
- for (let j = 0; j < coordinates[i].length; j++) {
75
- const coord = getTileCoordinates(coordinates[i][j], canonical);
74
+ for (let j = 0; j < coordinates[i]!.length; j++) {
75
+ const coord = getTileCoordinates(coordinates[i]![j]!, canonical);
76
76
  updateBBox(bbox, coord);
77
77
  ring.push(coord);
78
78
  }
@@ -85,14 +85,14 @@ function getTilePolygon(coordinates: Array<Array<GeoJSON.Position>>, bbox: BBox,
85
85
  function getTilePolygons(coordinates: Array<Array<Array<GeoJSON.Position>>>, bbox: BBox, canonical: CanonicalTileID): Array<Array<Array<number[]>>> {
86
86
  const polygons: Array<Array<Array<number[]>>> = [];
87
87
  for (let i = 0; i < coordinates.length; i++) {
88
- const polygon = getTilePolygon(coordinates[i], bbox, canonical);
88
+ const polygon = getTilePolygon(coordinates[i]!, bbox, canonical);
89
89
  polygons.push(polygon);
90
90
  }
91
91
 
92
92
  return polygons;
93
93
  }
94
94
 
95
- function updatePoint(p: GeoJSON.Position, bbox: BBox, polyBBox: Array<number>, worldSize: number) {
95
+ function updatePoint(p: [number, number], bbox: BBox, polyBBox: BBox, worldSize: number) {
96
96
  if (p[0] < polyBBox[0] || p[0] > polyBBox[2]) {
97
97
  const halfWorldSize = worldSize * 0.5;
98
98
  let shift = (p[0] - polyBBox[0] > halfWorldSize) ? -worldSize : (polyBBox[0] - p[0] > halfWorldSize) ? worldSize : 0;
@@ -109,14 +109,14 @@ function resetBBox(bbox: BBox) {
109
109
  bbox[2] = bbox[3] = -Infinity;
110
110
  }
111
111
 
112
- function getTilePoints(geometry: Array<Array<Point>> | null | undefined, pointBBox: BBox, polyBBox: Array<number>, canonical: CanonicalTileID): Array<number[]> {
112
+ function getTilePoints(geometry: Array<Array<Point>> | null | undefined, pointBBox: BBox, polyBBox: BBox, canonical: CanonicalTileID): Array<number[]> {
113
113
  const worldSize = Math.pow(2, canonical.z) * EXTENT;
114
- const shifts = [canonical.x * EXTENT, canonical.y * EXTENT];
114
+ const shifts: [number, number] = [canonical.x * EXTENT, canonical.y * EXTENT];
115
115
  const tilePoints: Array<number[]> = [];
116
116
  if (!geometry) return tilePoints;
117
117
  for (const points of geometry) {
118
118
  for (const point of points) {
119
- const p = [point.x + shifts[0], point.y + shifts[1]];
119
+ const p: [number, number] = [point.x + shifts[0], point.y + shifts[1]];
120
120
  updatePoint(p, pointBBox, polyBBox, worldSize);
121
121
  tilePoints.push(p);
122
122
  }
@@ -125,19 +125,18 @@ function getTilePoints(geometry: Array<Array<Point>> | null | undefined, pointBB
125
125
  return tilePoints;
126
126
  }
127
127
 
128
- function getTileLines(geometry: Array<Array<Point>> | null | undefined, lineBBox: BBox, polyBBox: Array<number>, canonical: CanonicalTileID): Array<Array<GeoJSON.Position>> {
128
+ function getTileLines(geometry: Array<Array<Point>> | null | undefined, lineBBox: BBox, polyBBox: BBox, canonical: CanonicalTileID): Array<Array<GeoJSON.Position>> {
129
129
  const worldSize = Math.pow(2, canonical.z) * EXTENT;
130
- const shifts = [canonical.x * EXTENT, canonical.y * EXTENT];
131
- const tileLines: Array<Array<GeoJSON.Position>> = [];
130
+ const shifts: [number, number] = [canonical.x * EXTENT, canonical.y * EXTENT];
131
+ const tileLines: Array<Array<[number, number]>> = [];
132
132
  if (!geometry) return tileLines;
133
133
  for (const line of geometry) {
134
- const tileLine = [];
134
+ const tileLine: Array<[number, number]> = [];
135
135
  for (const point of line) {
136
- const p: GeoJSON.Position = [point.x + shifts[0], point.y + shifts[1]];
136
+ const p: [number, number] = [point.x + shifts[0], point.y + shifts[1]];
137
137
  updateBBox(lineBBox, p);
138
138
  tileLine.push(p);
139
139
  }
140
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
141
140
  tileLines.push(tileLine);
142
141
  }
143
142
  if (lineBBox[2] - lineBBox[0] <= worldSize / 2) {
@@ -230,9 +229,9 @@ class Within implements Expression {
230
229
  const geojson = args[1] as GeoJSON.GeoJSON;
231
230
  if (geojson.type === 'FeatureCollection') {
232
231
  for (let i = 0; i < geojson.features.length; ++i) {
233
- const type = geojson.features[i].geometry.type;
232
+ const type = geojson.features[i]!.geometry.type;
234
233
  if (type === 'Polygon' || type === 'MultiPolygon') {
235
- return new Within(geojson, geojson.features[i].geometry as GeoJSONPolygons);
234
+ return new Within(geojson, geojson.features[i]!.geometry as GeoJSONPolygons);
236
235
  }
237
236
  }
238
237
  } else if (geojson.type === 'Feature') {
@@ -11,7 +11,7 @@ import type {ConfigOptions, ConfigOptionValue} from '../types/config_options';
11
11
  const geometryTypes = ['Unknown', 'Point', 'LineString', 'Polygon'];
12
12
 
13
13
  class EvaluationContext {
14
- globals: GlobalProperties;
14
+ globals: GlobalProperties | null;
15
15
  feature: Feature | null | undefined;
16
16
  featureState: FeatureState | null | undefined;
17
17
  formattedSection: FormattedSection | null | undefined;
@@ -23,7 +23,7 @@ class EvaluationContext {
23
23
  options: ConfigOptions | null | undefined;
24
24
  iconImageUseTheme: string | null | undefined;
25
25
 
26
- constructor(scope?: string | null, options?: ConfigOptions | null, iconImageUseTheme?: string) {
26
+ constructor(scope?: string | null, options?: ConfigOptions | null, iconImageUseTheme?: string | null) {
27
27
  this.globals = null;
28
28
  this.feature = null;
29
29
  this.featureState = null;
@@ -42,7 +42,7 @@ class EvaluationContext {
42
42
  }
43
43
 
44
44
  geometryType(): null | string {
45
- return this.feature ? typeof this.feature.type === 'number' ? geometryTypes[this.feature.type] : this.feature.type : null;
45
+ return this.feature ? typeof this.feature.type === 'number' ? geometryTypes[this.feature.type]! : this.feature.type : null;
46
46
  }
47
47
 
48
48
  geometry(): Array<Array<Point>> | null | undefined {
@@ -58,7 +58,7 @@ class EvaluationContext {
58
58
  }
59
59
 
60
60
  measureLight(_: string): number {
61
- return this.globals.brightness || 0;
61
+ return this.globals!.brightness || 0;
62
62
  }
63
63
 
64
64
  distanceFromCenter(): number {
@@ -19,7 +19,7 @@ export interface Expression {
19
19
  serialize: () => SerializedExpression;
20
20
  }
21
21
 
22
- export type ExpressionParser = (args: ReadonlyArray<unknown>, context: ParsingContext) => Expression | void;
22
+ export type ExpressionParser = (args: ReadonlyArray<unknown>, context: ParsingContext) => Expression | null | void;
23
23
 
24
24
  export type ExpressionRegistration = {
25
25
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -21,7 +21,7 @@ import {
21
21
  } from '../util/properties';
22
22
  import {isFunction, createFunction} from '../function/index';
23
23
  import {Color} from './values';
24
- import {ColorType, StringType, NumberType, BooleanType, ValueType, FormattedType, ResolvedImageType, array} from './types';
24
+ import {ColorType, StringType, NumberType, BooleanType, ValueType, FormattedType, ResolvedImageType, ObjectType, array} from './types';
25
25
 
26
26
  import type {Type, EvaluationKind} from './types';
27
27
  import type {Value} from './values';
@@ -29,7 +29,7 @@ import type {Expression} from './expression';
29
29
  import type {StylePropertySpecification} from '../style-spec';
30
30
  import type {Result} from '../util/result';
31
31
  import type {InterpolationType} from './definitions/interpolate';
32
- import type {PropertyValueSpecification} from '../types';
32
+ import type {PropertyValueSpecification, OptionSpecification} from '../types';
33
33
  import type {FormattedSection} from './types/formatted';
34
34
  import type Point from '@mapbox/point-geometry';
35
35
  import type {CanonicalTileID} from '../types/tile_id';
@@ -66,17 +66,17 @@ export interface GlobalProperties {
66
66
 
67
67
  export class StyleExpression {
68
68
  expression: Expression;
69
- _scope?: string;
70
- _options?: ConfigOptions;
71
- _iconImageUseTheme?: string;
69
+ _scope?: string | null;
70
+ _options?: ConfigOptions | null;
71
+ _iconImageUseTheme?: string | null;
72
72
  _evaluator?: EvaluationContext;
73
73
  _defaultValue: Value;
74
74
  _warningHistory: {[key: string]: boolean};
75
- _enumValues?: {[_: string]: unknown};
75
+ _enumValues?: {[_: string]: unknown} | null;
76
76
  configDependencies: Set<string>;
77
77
  isIndoorDependent: boolean;
78
78
 
79
- constructor(expression: Expression, propertySpec?: StylePropertySpecification, scope?: string, options?: ConfigOptions, iconImageUseTheme?: string) {
79
+ constructor(expression: Expression, propertySpec?: StylePropertySpecification | null, scope?: string | null, options?: ConfigOptions | null, iconImageUseTheme?: string | null) {
80
80
  this.expression = expression;
81
81
  this._warningHistory = {};
82
82
  this._scope = scope;
@@ -100,16 +100,17 @@ export class StyleExpression {
100
100
  featureDistanceData?: FeatureDistanceData,
101
101
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
102
102
  ): any {
103
- this._evaluator.globals = globals;
104
- this._evaluator.feature = feature;
105
- this._evaluator.featureState = featureState;
106
- this._evaluator.canonical = canonical || null;
107
- this._evaluator.availableImages = availableImages || null;
108
- this._evaluator.formattedSection = formattedSection;
109
- this._evaluator.featureTileCoord = featureTileCoord || null;
110
- this._evaluator.featureDistanceData = featureDistanceData || null;
111
-
112
- return this.expression.evaluate(this._evaluator);
103
+ const evaluator = this._evaluator!;
104
+ evaluator.globals = globals;
105
+ evaluator.feature = feature;
106
+ evaluator.featureState = featureState;
107
+ evaluator.canonical = canonical || null;
108
+ evaluator.availableImages = availableImages || null;
109
+ evaluator.formattedSection = formattedSection;
110
+ evaluator.featureTileCoord = featureTileCoord || null;
111
+ evaluator.featureDistanceData = featureDistanceData || null;
112
+
113
+ return this.expression.evaluate(evaluator);
113
114
  }
114
115
 
115
116
  evaluate(
@@ -197,6 +198,26 @@ export function createExpression(
197
198
  return success(new StyleExpression(parsed, propertySpec, scope, options, iconImageUseTheme));
198
199
  }
199
200
 
201
+ // Parse a config option's default or value with the option's declared type
202
+ // fed to the expression parser. This drives implicit string→color coercion
203
+ // inside expressions (e.g. `["interpolate", ..., "hsl(...)"]` on a color
204
+ // option). Skipped for array options (the parser doesn't model the schema's
205
+ // `array: true` flag) and for primitive values (they keep their original
206
+ // literal shape so `getConfig` round-trips unchanged).
207
+ //
208
+ // A plain (non-array) object -- e.g. a GeoJSON geometry for an `object`-typed
209
+ // option -- isn't valid expression syntax on its own (the parser rejects bare
210
+ // objects to avoid ambiguity with legacy function specs); treat it like
211
+ // `["literal", {...}]` instead, so both a schema `default` and a runtime
212
+ // value can be written as a bare object.
213
+ export function createConfigExpression(value: unknown, option: OptionSpecification = {} as OptionSpecification): Result<StyleExpression, Array<ParsingError>> {
214
+ const propertySpec = (option.type && !option.array && Array.isArray(value)) ?
215
+ {type: option.type, 'property-type': 'data-constant'} as unknown as StylePropertySpecification :
216
+ undefined;
217
+ const isBareObject = value !== null && typeof value === 'object' && !Array.isArray(value);
218
+ return createExpression(isBareObject ? ['literal', value] : value, propertySpec);
219
+ }
220
+
200
221
  export class ZoomConstantExpression<Kind extends EvaluationKind> {
201
222
  kind: Kind;
202
223
  isStateDependent: boolean;
@@ -446,9 +467,9 @@ export class StylePropertyFunction<T> {
446
467
  _parameters: PropertyValueSpecification<T>;
447
468
  _specification: StylePropertySpecification;
448
469
 
449
- kind: EvaluationKind;
450
- evaluate: <T = unknown>(globals: GlobalProperties, feature?: Feature) => T;
451
- interpolationFactor: (input: number, lower: number, upper: number) => number | null | undefined;
470
+ kind!: EvaluationKind;
471
+ evaluate!: <T = unknown>(globals: GlobalProperties, feature?: Feature) => T;
472
+ interpolationFactor!: (input: number, lower: number, upper: number) => number | null | undefined;
452
473
  zoomStops: Array<number> | null | undefined;
453
474
 
454
475
  constructor(parameters: PropertyValueSpecification<T>, specification: StylePropertySpecification) {
@@ -556,7 +577,8 @@ function getExpectedType(spec: StylePropertySpecification): Type {
556
577
  enum: StringType,
557
578
  boolean: BooleanType,
558
579
  formatted: FormattedType,
559
- resolvedImage: ResolvedImageType
580
+ resolvedImage: ResolvedImageType,
581
+ object: ObjectType
560
582
  };
561
583
 
562
584
  if (spec.type === 'array') {
@@ -28,7 +28,7 @@ class ParsingContext {
28
28
  errors: Array<ParsingError>;
29
29
  _scope: string | null | undefined;
30
30
  options: ConfigOptions | null | undefined;
31
- iconImageUseTheme: string;
31
+ iconImageUseTheme: string | null | undefined;
32
32
 
33
33
  // The expected type of this expression. Provided only to allow Expression
34
34
  // implementations to infer argument types: Expression#parse() need not
@@ -44,7 +44,7 @@ class ParsingContext {
44
44
  errors: Array<ParsingError> = [],
45
45
  _scope?: string | null,
46
46
  options?: ConfigOptions | null,
47
- iconImageUseTheme?: string
47
+ iconImageUseTheme?: string | null
48
48
  ) {
49
49
  this.registry = registry;
50
50
  this.path = path;
@@ -18,8 +18,8 @@ export function findStopLessThanOrEqualTo(stops: Array<number>, input: number):
18
18
 
19
19
  while (lowerIndex <= upperIndex) {
20
20
  currentIndex = Math.floor((lowerIndex + upperIndex) / 2);
21
- currentValue = stops[currentIndex];
22
- nextValue = stops[currentIndex + 1];
21
+ currentValue = stops[currentIndex]!;
22
+ nextValue = stops[currentIndex + 1]!;
23
23
 
24
24
  if (currentValue <= input) {
25
25
  if (currentIndex === lastIndex || input < nextValue) { // Search complete
@@ -79,7 +79,7 @@ export function isValue(mixed: unknown): boolean {
79
79
  return true;
80
80
  } else if (typeof mixed === 'object') {
81
81
  for (const key in mixed) {
82
- if (!isValue(mixed[key])) {
82
+ if (!isValue((mixed as Record<string, unknown>)[key])) {
83
83
  return false;
84
84
  }
85
85
  }
@@ -108,7 +108,7 @@ export function typeOf(value: Value): Type {
108
108
  return ResolvedImageType;
109
109
  } else if (Array.isArray(value)) {
110
110
  const length = value.length;
111
- let itemType: Type;
111
+ let itemType: Type | undefined;
112
112
 
113
113
  for (const item of value) {
114
114
  // eslint-disable-next-line @typescript-eslint/no-unsafe-argument