@player-tools/dsl 0.10.1 → 0.11.0--canary.212.4707

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.
@@ -1,12 +1,19 @@
1
1
  import { test, expect, describe } from "vitest";
2
- import { getObjectReferences } from "../utils";
2
+ import {
3
+ getObjectReferences,
4
+ mapExpressionHandlersToFunctions,
5
+ wrapFunctionInType,
6
+ } from "../utils";
7
+ import { Binding, Expression, ExpressionHandler } from "@player-ui/player";
8
+ import { binding, expression } from "../string-templates";
9
+ import { LocalBazType } from "./helpers/mock-data-refs";
10
+ import { DataTypeRefs } from "../types";
11
+ import { makeBindingsForObject } from "../compiler/schema";
3
12
 
4
13
  describe("Testing the 'getObjectReferences' helper that creates same property references into a new object", () => {
5
14
  test("should return the object properties in referenced format", () => {
6
15
  const dataTypes = {
7
- BooleanType: {
8
- type: "BooleanType",
9
- },
16
+ LocalBazType,
10
17
  };
11
18
 
12
19
  const validators = {
@@ -15,12 +22,182 @@ describe("Testing the 'getObjectReferences' helper that creates same property re
15
22
  },
16
23
  };
17
24
 
18
- const dataReferences = getObjectReferences(dataTypes);
19
- const validatorReferences = getObjectReferences(validators);
25
+ type DTREF = DataTypeRefs<typeof dataTypes>;
26
+
27
+ const dataReferences = getObjectReferences<typeof dataTypes, DTREF>(
28
+ dataTypes,
29
+ );
20
30
 
21
- expect(dataReferences.BooleanTypeRef).toStrictEqual({
22
- type: "BooleanType",
31
+ type VALREF = DataTypeRefs<typeof validators>;
32
+ const validatorReferences = getObjectReferences<typeof validators, VALREF>(
33
+ validators,
34
+ );
35
+
36
+ expect(dataReferences.LocalBazTypeRef).toStrictEqual({
37
+ type: "LocalBazType",
23
38
  });
24
39
  expect(validatorReferences.requiredRef).toStrictEqual({ type: "required" });
25
40
  });
26
41
  });
42
+
43
+ describe("DSL Expression Generation Helper", () => {
44
+ test("Returns the correct serialization for bindings", () => {
45
+ const mockFunction: ExpressionHandler<[Binding], boolean> = (ctx, val) => {
46
+ return false;
47
+ };
48
+
49
+ const mockFunctionDSL = wrapFunctionInType(mockFunction);
50
+
51
+ const foo = binding`some.binding`;
52
+ const test = expression`${mockFunctionDSL(foo)} == true`;
53
+
54
+ expect(test.toValue()).toEqual("mockFunction('some.binding') == true");
55
+ });
56
+
57
+ test("Returns the correct serialization for mixed values and bindings", () => {
58
+ const mockFunction: ExpressionHandler<[string, string, number], boolean> = (
59
+ ctx,
60
+ val,
61
+ ) => {
62
+ return false;
63
+ };
64
+
65
+ const mockFunctionDSL = wrapFunctionInType(mockFunction);
66
+
67
+ const foo = binding`some.binding`;
68
+ const test = expression`${mockFunctionDSL(foo.toRefString(), "test", 1)} == true`;
69
+
70
+ expect(test.toValue()).toEqual(
71
+ "mockFunction('{{some.binding}}', 'test', 1) == true",
72
+ );
73
+ });
74
+
75
+ test("Returns the correct serialization for array items", () => {
76
+ const mockFunction: ExpressionHandler<[Array<unknown>], boolean> = (
77
+ ctx,
78
+ val,
79
+ ) => {
80
+ return false;
81
+ };
82
+
83
+ const mockFunctionDSL = wrapFunctionInType(mockFunction);
84
+
85
+ const foo = binding`some.binding`;
86
+ const test = expression`${mockFunctionDSL([1, "2", foo])}`;
87
+
88
+ expect(test.toValue()).toEqual(
89
+ "mockFunction([1, '2', '{{some.binding}}'])",
90
+ );
91
+ });
92
+
93
+ test("Can Dereferenced Binding", () => {
94
+ const mockFunction: ExpressionHandler<[boolean], boolean> = (ctx, val) => {
95
+ return false;
96
+ };
97
+
98
+ const mockFunctionDSL = wrapFunctionInType(mockFunction);
99
+
100
+ const foo = binding`some.binding`;
101
+ const test = expression`${mockFunctionDSL(foo.toRefString())} == true`;
102
+
103
+ expect(test.toValue()).toEqual("mockFunction('{{some.binding}}') == true");
104
+ });
105
+
106
+ test("Can Pass Nested Expressions", () => {
107
+ const mockFunction: ExpressionHandler<[Expression, number], boolean> = (
108
+ ctx,
109
+ val,
110
+ ) => {
111
+ return false;
112
+ };
113
+
114
+ const mockFunctionDSL = wrapFunctionInType(mockFunction);
115
+
116
+ const foo = binding`foo.bar`;
117
+ const bar = expression`${foo} != 1`;
118
+ const test = expression`${mockFunctionDSL(bar, 1)}`;
119
+
120
+ expect(test.toValue()).toEqual("mockFunction('{{foo.bar}} != 1', 1)");
121
+ });
122
+
123
+ test("Export Generator", () => {
124
+ const mockFunction: ExpressionHandler<[string, number], boolean> = (
125
+ ctx,
126
+ val,
127
+ ) => {
128
+ return false;
129
+ };
130
+ const expressionFunctions = { mockFunction };
131
+
132
+ const usableFunctions =
133
+ mapExpressionHandlersToFunctions<typeof expressionFunctions>(
134
+ expressionFunctions,
135
+ );
136
+
137
+ expect(usableFunctions.mockFunction("1", 0).toValue()).toMatch(
138
+ "mockFunction('1', 0)",
139
+ );
140
+ });
141
+
142
+ test("Return Type Chaining", () => {
143
+ const mockFunction: ExpressionHandler<[string, number], string> = (
144
+ ctx,
145
+ val,
146
+ ) => {
147
+ return "false";
148
+ };
149
+
150
+ const mockFunction2: ExpressionHandler<[string], boolean> = (ctx, val) => {
151
+ return false;
152
+ };
153
+ const expressionFunctions = { mockFunction, mockFunction2 };
154
+
155
+ const usableFunctions =
156
+ mapExpressionHandlersToFunctions<typeof expressionFunctions>(
157
+ expressionFunctions,
158
+ );
159
+
160
+ expect(
161
+ usableFunctions
162
+ .mockFunction2(usableFunctions.mockFunction("1", 0).toValue())
163
+ .toValue(),
164
+ ).toMatch("mockFunction2('mockFunction('1', 0)')");
165
+ });
166
+
167
+ test("Works with DSL Schema", () => {
168
+ const mockFunction: ExpressionHandler<[string, boolean], string> = (
169
+ ctx,
170
+ val,
171
+ ) => {
172
+ return "false";
173
+ };
174
+
175
+ const dataTypes = { LocalBazType };
176
+
177
+ type DTREF = DataTypeRefs<typeof dataTypes>;
178
+
179
+ const refableTypes = getObjectReferences<typeof dataTypes, DTREF>(
180
+ dataTypes,
181
+ );
182
+
183
+ const data = {
184
+ some: {
185
+ value: refableTypes.LocalBazTypeRef,
186
+ },
187
+ };
188
+
189
+ const schema = makeBindingsForObject(data);
190
+ const expressionFunctions = { mockFunction };
191
+
192
+ const usableFunctions =
193
+ mapExpressionHandlersToFunctions<typeof expressionFunctions>(
194
+ expressionFunctions,
195
+ );
196
+
197
+ expect(
198
+ usableFunctions
199
+ .mockFunction("1", schema.some.value.toRefString())
200
+ .toValue(),
201
+ ).toMatch("mockFunction('1', '{{some.value}}')");
202
+ });
203
+ });
@@ -8,7 +8,7 @@ import type { BindingTemplateInstance } from "../string-templates";
8
8
  const bindingSymbol = Symbol("binding");
9
9
 
10
10
  /** Symbol to indicate that a schema node should be generated with a different name */
11
- export const SchemaTypeName = Symbol("Schema Rename");
11
+ export const SchemaTypeName: unique symbol = Symbol("Schema Rename");
12
12
 
13
13
  interface SchemaChildren {
14
14
  /** Object property that will be used to create the intermediate type */
@@ -47,7 +47,15 @@ export class SchemaGenerator {
47
47
  private generatedDataTypes: Map<string, GeneratedDataType>;
48
48
  private logger: LoggingInterface;
49
49
 
50
- public hooks = {
50
+ public hooks: {
51
+ createSchemaNode: SyncWaterfallHook<
52
+ [
53
+ node: Schema.DataType<unknown>,
54
+ originalProperty: Record<string | symbol, unknown>,
55
+ ],
56
+ Record<string, any>
57
+ >;
58
+ } = {
51
59
  createSchemaNode: new SyncWaterfallHook<
52
60
  [
53
61
  node: Schema.DataType,
@@ -197,14 +205,14 @@ export type MakeBindingRefable<T> = {
197
205
  : T[P] extends unknown[]
198
206
  ? T[P]
199
207
  : MakeBindingRefable<T[P]>;
200
- } & BindingTemplateInstance;
208
+ } & BindingTemplateInstance<T>;
201
209
 
202
210
  /**
203
211
  * Adds bindings to an object so that the object can be directly used in JSX
204
212
  */
205
213
  export function makeBindingsForObject<Type>(
206
214
  obj: Type,
207
- arrayAccessorKeys = ["_index_"],
215
+ arrayAccessorKeys: string[] = ["_index_"],
208
216
  ): MakeBindingRefable<Type> {
209
217
  /** Proxy to track binding callbacks */
210
218
  const accessor = (paths: string[]) => {
@@ -258,7 +266,7 @@ export function makeBindingsForObject<Type>(
258
266
  /**
259
267
  * Generates binding for an object property
260
268
  */
261
- export const getBindingFromObject = (obj: any) => {
269
+ export const getBindingFromObject = (obj: any): BindingTemplateInstance => {
262
270
  const baseBindings = obj[bindingSymbol] as string[];
263
271
  if (!Array.isArray(baseBindings) || baseBindings.length === 0) {
264
272
  throw new Error(`Unable to get binding for ${obj}`);
@@ -270,13 +278,13 @@ export const getBindingFromObject = (obj: any) => {
270
278
  /**
271
279
  * Returns the binding string from an object path
272
280
  */
273
- export const getBindingStringFromObject = (obj: any) => {
281
+ export const getBindingStringFromObject = (obj: any): string => {
274
282
  return getBindingFromObject(obj).toString();
275
283
  };
276
284
 
277
285
  /**
278
286
  * Returns the ref string from an object path
279
287
  */
280
- export const getRefStringFromObject = (obj: any) => {
288
+ export const getRefStringFromObject = (obj: any): string => {
281
289
  return getBindingFromObject(obj).toRefString();
282
290
  };
@@ -0,0 +1,159 @@
1
+ import { test, expect, describe } from "vitest";
2
+ import {
3
+ and,
4
+ assign,
5
+ binding as b,
6
+ expression as e,
7
+ equals,
8
+ nand,
9
+ nor,
10
+ not,
11
+ or,
12
+ } from "../../";
13
+
14
+ describe("assign", () => {
15
+ test("string", () => {
16
+ const binding = b`foo.bar`;
17
+ expect(assign(binding, "1").toValue()).toStrictEqual("{{foo.bar}} = '1'");
18
+ });
19
+ test("number", () => {
20
+ const binding = b`foo.bar`;
21
+ expect(assign(binding, 1).toValue()).toStrictEqual("{{foo.bar}} = 1");
22
+ });
23
+ test("boolean", () => {
24
+ const binding = b`foo.bar`;
25
+ expect(assign(binding, true).toValue()).toStrictEqual("{{foo.bar}} = true");
26
+ });
27
+ test("undefined", () => {
28
+ const binding = b`foo.bar`;
29
+ expect(assign(binding, undefined).toValue()).toStrictEqual(
30
+ "{{foo.bar}} = null",
31
+ );
32
+ });
33
+ });
34
+
35
+ describe("not", () => {
36
+ test("binding", () => {
37
+ const binding = b<boolean>`foo.bar`;
38
+ expect(not(binding).toValue()).toStrictEqual("!({{foo.bar}})");
39
+ });
40
+ test("expression", () => {
41
+ const expression = e<boolean>`${b`foo.bar`} = true`;
42
+
43
+ expect(not(expression).toValue()).toStrictEqual("!({{foo.bar}} = true)");
44
+ });
45
+ });
46
+
47
+ describe("and", () => {
48
+ test("bindings", () => {
49
+ const binding = b<boolean>`foo.bar`;
50
+ const binding2 = b<boolean>`foo.baz`;
51
+ expect(and(binding, binding2).toValue()).toStrictEqual(
52
+ "{{foo.bar}} && {{foo.baz}}",
53
+ );
54
+ });
55
+
56
+ test("expressions", () => {
57
+ const expression = e<boolean>`${b`foo.bar`} == true`;
58
+ const expression2 = e<boolean>`${b`foo.baz`} == '1'`;
59
+ expect(and(expression, expression2).toValue()).toStrictEqual(
60
+ "({{foo.bar}} == true) && ({{foo.baz}} == '1')",
61
+ );
62
+ });
63
+ });
64
+
65
+ describe("or", () => {
66
+ test("bindings", () => {
67
+ const binding = b<boolean>`foo.bar`;
68
+ const binding2 = b<boolean>`foo.baz`;
69
+ expect(or(binding, binding2).toValue()).toStrictEqual(
70
+ "{{foo.bar}} || {{foo.baz}}",
71
+ );
72
+ });
73
+
74
+ test("expressions", () => {
75
+ const expression = e<boolean>`${b`foo.bar`} == true`;
76
+ const expression2 = e<boolean>`${b`foo.baz`} == '1'`;
77
+ expect(or(expression, expression2).toValue()).toStrictEqual(
78
+ "({{foo.bar}} == true) || ({{foo.baz}} == '1')",
79
+ );
80
+ });
81
+ });
82
+
83
+ describe("nand", () => {
84
+ test("bindings", () => {
85
+ const binding = b<boolean>`foo.bar`;
86
+ const binding2 = b<boolean>`foo.baz`;
87
+ expect(nand(binding, binding2).toValue()).toStrictEqual(
88
+ "!({{foo.bar}} && {{foo.baz}})",
89
+ );
90
+ });
91
+
92
+ test("expressions", () => {
93
+ const expression = e<boolean>`${b`foo.bar`} == true`;
94
+ const expression2 = e<boolean>`${b`foo.baz`} == '1'`;
95
+ expect(nand(expression, expression2).toValue()).toStrictEqual(
96
+ "!(({{foo.bar}} == true) && ({{foo.baz}} == '1'))",
97
+ );
98
+ });
99
+ });
100
+
101
+ describe("nor", () => {
102
+ test("bindings", () => {
103
+ const binding = b<boolean>`foo.bar`;
104
+ const binding2 = b<boolean>`foo.baz`;
105
+ expect(nor(binding, binding2).toValue()).toStrictEqual(
106
+ "!({{foo.bar}} || {{foo.baz}})",
107
+ );
108
+ });
109
+
110
+ test("expressions", () => {
111
+ const expression = e<boolean>`${b`foo.bar`} == true`;
112
+ const expression2 = e<boolean>`${b`foo.baz`} == '1'`;
113
+ expect(nor(expression, expression2).toValue()).toStrictEqual(
114
+ "!(({{foo.bar}} == true) || ({{foo.baz}} == '1'))",
115
+ );
116
+ });
117
+ });
118
+
119
+ describe("equals", () => {
120
+ test("binding-string", () => {
121
+ const binding = b`foo.bar`;
122
+ expect(equals(binding, "1").toValue()).toStrictEqual("{{foo.bar}} == '1'");
123
+ });
124
+
125
+ test("binding-number", () => {
126
+ const binding = b`foo.bar`;
127
+ expect(equals(binding, 1).toValue()).toStrictEqual("{{foo.bar}} == 1");
128
+ });
129
+
130
+ test("binding-boolean", () => {
131
+ const binding = b`foo.bar`;
132
+ expect(equals(binding, true).toValue()).toStrictEqual(
133
+ "{{foo.bar}} == true",
134
+ );
135
+ });
136
+
137
+ test("binding-undefined", () => {
138
+ const binding = b`foo.bar`;
139
+ expect(equals(binding, undefined).toValue()).toStrictEqual(
140
+ "{{foo.bar}} == null",
141
+ );
142
+ });
143
+
144
+ test("bindings", () => {
145
+ const binding = b<boolean>`foo.bar`;
146
+ const binding2 = b<boolean>`foo.baz`;
147
+ expect(equals(binding, binding2).toValue()).toStrictEqual(
148
+ "{{foo.bar}} == {{foo.baz}}",
149
+ );
150
+ });
151
+
152
+ test("expressions", () => {
153
+ const expression = e<boolean>`${b`foo.bar`} == true`;
154
+ const expression2 = e<boolean>`${b`foo.baz`} == '1'`;
155
+ expect(equals(expression, expression2).toValue()).toStrictEqual(
156
+ "({{foo.bar}} == true) == ({{foo.baz}} == '1')",
157
+ );
158
+ });
159
+ });
@@ -0,0 +1,141 @@
1
+ import {
2
+ expression as e,
3
+ BindingTemplateInstance,
4
+ ExpressionTemplateInstance,
5
+ isTemplateStringInstance,
6
+ isBindingTemplateInstance,
7
+ } from "..";
8
+
9
+ type Argument<T> =
10
+ | string
11
+ | boolean
12
+ | number
13
+ | undefined
14
+ | BindingTemplateInstance<T>
15
+ | ExpressionTemplateInstance<T>;
16
+
17
+ const escapePrimitive = <T>(
18
+ val: Exclude<
19
+ Argument<T>,
20
+ BindingTemplateInstance<T> | ExpressionTemplateInstance<T>
21
+ >,
22
+ ): string => {
23
+ switch (typeof val) {
24
+ case "string": {
25
+ return `'${val}'`;
26
+ }
27
+ case "undefined": {
28
+ return "null";
29
+ }
30
+ default: {
31
+ return `${val}`;
32
+ }
33
+ }
34
+ };
35
+
36
+ const handleBindingOrExpression = (
37
+ val: BindingTemplateInstance | ExpressionTemplateInstance,
38
+ ) => {
39
+ if (isBindingTemplateInstance(val)) {
40
+ return val.toRefString();
41
+ } else {
42
+ return `(${val.toValue()})`;
43
+ }
44
+ };
45
+
46
+ const handleArgument = <T>(arg: Argument<T>): string => {
47
+ return isTemplateStringInstance(arg)
48
+ ? handleBindingOrExpression(arg)
49
+ : escapePrimitive(arg);
50
+ };
51
+
52
+ /**
53
+ * Performs an assigment of a value to a binding by returning the expression
54
+ * {{<binding>}} = <value>
55
+ * @param binding
56
+ * @param value
57
+ */
58
+ export const assign = <T>(
59
+ binding: BindingTemplateInstance<any>,
60
+ value: Argument<T>,
61
+ ): ExpressionTemplateInstance<void> => {
62
+ return e`${binding} = ${isTemplateStringInstance(value) ? value : escapePrimitive(value)}`;
63
+ };
64
+
65
+ /**
66
+ * Returns an equality comparison between the two values
67
+ */
68
+ export const equals = <A, B>(
69
+ a: Argument<A>,
70
+ b: Argument<B>,
71
+ ): ExpressionTemplateInstance<boolean> => {
72
+ return e`${handleArgument(a)} == ${handleArgument(b)}`;
73
+ };
74
+
75
+ /**
76
+ * Returns the negated version of the binding/expression
77
+ * by returning !(<value>)
78
+ * @param binding Binding/Expression to invert
79
+ * @returns Negated binding/expression
80
+ */
81
+ export const not = (
82
+ value: BindingTemplateInstance<boolean> | ExpressionTemplateInstance<boolean>,
83
+ ): ExpressionTemplateInstance<boolean> => {
84
+ return e<boolean>`!(${value})`;
85
+ };
86
+
87
+ /**
88
+ * Creates an expression for the logical or'ing of the provided values
89
+ * e.g: <exp1> || <exp2> || ...
90
+ * @param values Array of bindings/expressions to logically or
91
+ * @returns boolean
92
+ */
93
+ export const or = (
94
+ ...values: Array<
95
+ BindingTemplateInstance<boolean> | ExpressionTemplateInstance<boolean>
96
+ >
97
+ ): ExpressionTemplateInstance<boolean> => {
98
+ return e`${values.map(handleBindingOrExpression).join(" || ")}` as ExpressionTemplateInstance<boolean>;
99
+ };
100
+
101
+ /**
102
+ * Creates an expression for the logical nor'ing of the provided values
103
+ * e.g: !(<exp1> || <exp2> || ...)
104
+ * @param values Array of bindings/expressions to logically nor
105
+ * @returns boolean
106
+ */
107
+ export const nor = (
108
+ ...values: Array<
109
+ BindingTemplateInstance<boolean> | ExpressionTemplateInstance<boolean>
110
+ >
111
+ ): ExpressionTemplateInstance<boolean> => {
112
+ return not(or(...values));
113
+ };
114
+
115
+ /**
116
+ * Creates an expression for the logical and'ing of the provided values
117
+ * e.g: <exp1> && <exp2> && ...
118
+ * @param values Array of bindings/expressions to logically and
119
+ * @returns boolean
120
+ */
121
+ export const and = (
122
+ ...values: Array<
123
+ BindingTemplateInstance<boolean> | ExpressionTemplateInstance<boolean>
124
+ >
125
+ ): ExpressionTemplateInstance<boolean> => {
126
+ return e`${values.map(handleBindingOrExpression).join(" && ")}` as ExpressionTemplateInstance<boolean>;
127
+ };
128
+
129
+ /**
130
+ * Creates an expression for the logical nand'ing of the provided values
131
+ * e.g: !(<exp1> && <exp2> && ...)
132
+ * @param values Array of bindings/expressions to logically nand
133
+ * @returns boolean
134
+ */
135
+ export const nand = (
136
+ ...values: Array<
137
+ BindingTemplateInstance<boolean> | ExpressionTemplateInstance<boolean>
138
+ >
139
+ ): ExpressionTemplateInstance<boolean> => {
140
+ return not(and(...values));
141
+ };
package/src/index.ts CHANGED
@@ -9,3 +9,4 @@ export * from "react-json-reconciler";
9
9
  export * from "./compiler/schema";
10
10
  export * from "./compiler";
11
11
  export * from "./compiler/types";
12
+ export * from "./expressions/native";
@@ -22,9 +22,11 @@ export interface TemplateInstanceRefStringOptions {
22
22
  ) => string;
23
23
  }
24
24
 
25
- const OpaqueIdentifier = Symbol("TemplateStringType");
25
+ const OpaqueIdentifier: unique symbol = Symbol("TemplateStringType");
26
26
 
27
- export type TemplateStringType = React.ReactElement & {
27
+ export type TemplateStringType<
28
+ T extends string | number | boolean | unknown = any,
29
+ > = React.ReactElement & {
28
30
  /** An identifier to show that this is a template type */
29
31
  [OpaqueIdentifier]: true;
30
32
  /** The value of the template string when in another string */
@@ -32,15 +34,21 @@ export type TemplateStringType = React.ReactElement & {
32
34
  /** the raw value of the template string */
33
35
  toValue: () => string;
34
36
  /** the dereferenced value when used in another */
35
- toRefString: (options?: TemplateRefStringOptions) => string;
37
+ toRefString: (options?: TemplateRefStringOptions) => T;
38
+ /** Underlying type of this binding */
39
+ type: T;
36
40
  };
37
41
 
38
- export type BindingTemplateInstance = TemplateStringType & {
42
+ export type BindingTemplateInstance<
43
+ DataType extends string | number | boolean | unknown = any,
44
+ > = TemplateStringType<DataType> & {
39
45
  /** An identifier for a binding instance */
40
46
  __type: "binding";
41
47
  };
42
48
 
43
- export type ExpressionTemplateInstance = TemplateStringType & {
49
+ export type ExpressionTemplateInstance<
50
+ ReturnType extends string | number | boolean | unknown = any,
51
+ > = TemplateStringType<ReturnType> & {
44
52
  /** The identifier for an expression instance */
45
53
  __type: "expression";
46
54
  };
@@ -49,7 +57,9 @@ export type ExpressionTemplateInstance = TemplateStringType & {
49
57
  export const TemplateStringComponent = (props: {
50
58
  /** The string value of the child template string */
51
59
  value: string;
52
- }) => {
60
+ }): React.ReactElement<{
61
+ value: string;
62
+ }> => {
53
63
  return React.createElement(
54
64
  "value",
55
65
  {
@@ -179,10 +189,10 @@ const createExpressionTemplateInstance = (
179
189
  };
180
190
 
181
191
  /** A tagged-template constructor for a binding */
182
- export const binding = (
192
+ export const binding = <T>(
183
193
  strings: TemplateStringsArray,
184
194
  ...nested: Array<TemplateStringType | string>
185
- ): BindingTemplateInstance => {
195
+ ): BindingTemplateInstance<T> => {
186
196
  return createBindingTemplateInstance({
187
197
  strings,
188
198
  other: nested,
@@ -191,12 +201,12 @@ export const binding = (
191
201
  };
192
202
 
193
203
  /** A tagged-template constructor for an expression */
194
- export const expression = (
204
+ export const expression = <T>(
195
205
  strings: TemplateStringsArray,
196
206
  ...nested: Array<
197
207
  ExpressionTemplateInstance | BindingTemplateInstance | string
198
208
  >
199
- ): ExpressionTemplateInstance => {
209
+ ): ExpressionTemplateInstance<T> => {
200
210
  return createExpressionTemplateInstance({
201
211
  strings,
202
212
  other: nested,