diff --git a/src/awst_build/arc4-util.ts b/src/awst_build/arc4-util.ts index 24aafed00..48540f1f5 100644 --- a/src/awst_build/arc4-util.ts +++ b/src/awst_build/arc4-util.ts @@ -161,8 +161,9 @@ export function isArc4EncodableType(ptype: PType): boolean { if (ptype.equals(accountPType)) return true if (ptype instanceof ReadonlyTuplePType) return ptype.items.every((i) => isArc4EncodableType(i)) if (ptype instanceof MutableTuplePType) return ptype.items.every((i) => isArc4EncodableType(i)) - if (ptype instanceof ImmutableObjectPType) return ptype.orderedProperties().every(([, pt]) => isArc4EncodableType(pt)) - if (ptype instanceof MutableObjectPType) return ptype.orderedProperties().every(([, pt]) => isArc4EncodableType(pt)) + if (ptype instanceof ImmutableObjectPType) + return !ptype.runtimeOnly && ptype.orderedProperties().every(([, pt]) => isArc4EncodableType(pt)) + if (ptype instanceof MutableObjectPType) return !ptype.runtimeOnly && ptype.orderedProperties().every(([, pt]) => isArc4EncodableType(pt)) if (ptype instanceof ArrayPType || ptype instanceof FixedArrayPType || ptype instanceof ReadonlyArrayPType) return isArc4EncodableType(ptype.elementType) return false @@ -216,7 +217,7 @@ export function ptypeToArc4EncodedType(ptype: PType, sourceLocation: SourceLocat if (ptype instanceof MutableTuplePType) return new ARC4TupleType({ types: ptype.items.map((i) => ptypeToArc4EncodedType(i, sourceLocation)) }) - if (ptype instanceof ImmutableObjectPType) + if (ptype instanceof ImmutableObjectPType && !ptype.runtimeOnly) return new ARC4StructType({ name: ptype.alias?.name ?? ptype.name, module: ptype.module, @@ -224,7 +225,7 @@ export function ptypeToArc4EncodedType(ptype: PType, sourceLocation: SourceLocat fields: Object.fromEntries(ptype.orderedProperties().map(([p, pt]) => [p, ptypeToArc4EncodedType(pt, sourceLocation)])), frozen: true, }) - if (ptype instanceof MutableObjectPType) + if (ptype instanceof MutableObjectPType && !ptype.runtimeOnly) return new ARC4StructType({ name: ptype.alias?.name ?? ptype.name, module: ptype.module, diff --git a/src/awst_build/context/awst-build-context.ts b/src/awst_build/context/awst-build-context.ts index cb63688fc..2fbc65809 100644 --- a/src/awst_build/context/awst-build-context.ts +++ b/src/awst_build/context/awst-build-context.ts @@ -4,6 +4,7 @@ import type { ContractReference, LogicSigReference } from '../../awst/models' import { nodeFactory } from '../../awst/node-factory' import type { AppStorageDefinition, ARC4MethodConfig } from '../../awst/nodes' import { SourceLocation } from '../../awst/source-location' +import { PuyaError } from '../../errors' import { logger } from '../../logger' import { invariant } from '../../util' import { AbsolutePath } from '../../util/absolute-path' @@ -337,7 +338,15 @@ class AwstBuildContextImpl extends AwstBuildContext { `Redefinition of app storage member, original declared in ${declaration.sourceLocation}`, ) } - result.set(memberName, declaration.definition) + try { + result.set(memberName, declaration.definition) + } catch (e) { + if (e instanceof PuyaError) { + logger.error(e) + } else { + throw e + } + } } } return Array.from(result.values()) diff --git a/src/awst_build/eb/arc4/struct.ts b/src/awst_build/eb/arc4/struct.ts index 37e5a12dc..8c66141d9 100644 --- a/src/awst_build/eb/arc4/struct.ts +++ b/src/awst_build/eb/arc4/struct.ts @@ -30,7 +30,7 @@ export class StructClassBuilder extends ClassBuilder { genericTypeArgs: 1, callLocation: sourceLocation, funcName: this.typeDescription, - argSpec: (a) => [a.required(new ImmutableObjectPType({ properties: this.ptype.instanceType.fields }))], + argSpec: (a) => [a.required(new ImmutableObjectPType({ properties: this.ptype.instanceType.fields, runtimeOnly: false }))], }) const initialSingle = initialValues.singleEvaluation() diff --git a/src/awst_build/eb/storage/box/box-map.ts b/src/awst_build/eb/storage/box/box-map.ts index f06791526..512ce15f5 100644 --- a/src/awst_build/eb/storage/box/box-map.ts +++ b/src/awst_build/eb/storage/box/box-map.ts @@ -9,7 +9,7 @@ import type { PType } from '../../../ptypes' import { BoxMapPType, BoxPType, bytesPType, stringPType } from '../../../ptypes' import { FunctionBuilder, type NodeBuilder } from '../../index' import { parseFunctionArgs } from '../../util/arg-parsing' -import { extractKey } from '../util' +import { assertCanBeUsedForStorage, extractKey } from '../util' import { BoxProxyExpressionBuilder } from './base' import { BoxExpressionBuilder } from './box' @@ -26,6 +26,7 @@ export class BoxMapFunctionBuilder extends FunctionBuilder { genericTypeArgs: 2, argSpec: (a) => [a.obj({ keyPrefix: a.required(bytesPType, stringPType) })], }) + assertCanBeUsedForStorage(contentPType, sourceLocation) const ptype = new BoxMapPType({ content: contentPType, keyType: keySuffixType }) return new BoxMapExpressionBuilder(extractKey(keyPrefix, wtypes.boxKeyWType, sourceLocation), ptype) diff --git a/src/awst_build/eb/storage/box/box.ts b/src/awst_build/eb/storage/box/box.ts index 398ce52e8..27b1e2e52 100644 --- a/src/awst_build/eb/storage/box/box.ts +++ b/src/awst_build/eb/storage/box/box.ts @@ -9,7 +9,7 @@ import { boolPType, BoxPType, bytesPType, ReadonlyTuplePType, stringPType, uint6 import { instanceEb } from '../../../type-registry' import { FunctionBuilder, type NodeBuilder } from '../../index' import { parseFunctionArgs } from '../../util/arg-parsing' -import { extractKey } from '../util' +import { assertCanBeUsedForStorage, extractKey } from '../util' import { boxExists, boxLength, BoxProxyExpressionBuilder, boxValue, BoxValueExpressionBuilder } from './base' export class BoxFunctionBuilder extends FunctionBuilder { @@ -25,6 +25,7 @@ export class BoxFunctionBuilder extends FunctionBuilder { genericTypeArgs: 1, argSpec: (a) => [a.obj({ key: a.required(bytesPType, stringPType) })], }) + assertCanBeUsedForStorage(contentPType, sourceLocation) const ptype = new BoxPType({ content: contentPType }) return new BoxExpressionBuilder(extractKey(key, wtypes.boxKeyWType, sourceLocation), ptype) diff --git a/src/awst_build/eb/storage/global-state.ts b/src/awst_build/eb/storage/global-state.ts index 02e2cf2bd..a1be784fa 100644 --- a/src/awst_build/eb/storage/global-state.ts +++ b/src/awst_build/eb/storage/global-state.ts @@ -16,7 +16,7 @@ import type { NodeBuilder } from '../index' import { FunctionBuilder, InstanceExpressionBuilder } from '../index' import { parseFunctionArgs } from '../util/arg-parsing' import { VoidExpressionBuilder } from '../void-expression-builder' -import { extractKey } from './util' +import { assertCanBeUsedForStorage, extractKey } from './util' export class GlobalStateFunctionBuilder extends FunctionBuilder { constructor(sourceLocation: SourceLocation) { @@ -28,6 +28,7 @@ export class GlobalStateFunctionBuilder extends FunctionBuilder { if (ptype.contentType.equals(numberPType)) { logger.addCodeFix(new GlobalStateNumber({ sourceLocation })) } + assertCanBeUsedForStorage(ptype.contentType, sourceLocation) const { args: [{ initialValue, key }], } = parseFunctionArgs({ diff --git a/src/awst_build/eb/storage/local-state.ts b/src/awst_build/eb/storage/local-state.ts index 6f1a028a2..88c0560e0 100644 --- a/src/awst_build/eb/storage/local-state.ts +++ b/src/awst_build/eb/storage/local-state.ts @@ -14,7 +14,7 @@ import { instanceEb } from '../../type-registry' import { FunctionBuilder, InstanceBuilder, InstanceExpressionBuilder, NodeBuilder } from '../index' import { parseFunctionArgs } from '../util/arg-parsing' import { VoidExpressionBuilder } from '../void-expression-builder' -import { extractKey } from './util' +import { assertCanBeUsedForStorage, extractKey } from './util' export class LocalStateFunctionBuilder extends FunctionBuilder { constructor(sourceLocation: SourceLocation) { @@ -23,6 +23,7 @@ export class LocalStateFunctionBuilder extends FunctionBuilder { call(args: ReadonlyArray, typeArgs: ReadonlyArray, sourceLocation: SourceLocation): NodeBuilder { const [contentPType] = typeArgs + assertCanBeUsedForStorage(contentPType, sourceLocation) const { args: [{ key }], } = parseFunctionArgs({ diff --git a/src/awst_build/eb/storage/util.ts b/src/awst_build/eb/storage/util.ts index da3624382..6596ea515 100644 --- a/src/awst_build/eb/storage/util.ts +++ b/src/awst_build/eb/storage/util.ts @@ -3,6 +3,9 @@ import type { Expression } from '../../../awst/nodes' import { BytesConstant } from '../../../awst/nodes' import type { SourceLocation } from '../../../awst/source-location' import type { wtypes } from '../../../awst/wtypes' +import { CodeError } from '../../../errors' +import type { PType } from '../../ptypes' +import { ImmutableObjectPType, MutableObjectPType, TransientType, UnsupportedType } from '../../ptypes' import type { InstanceBuilder } from '../index' export function extractKey(key: InstanceBuilder, keyWType: wtypes.WType, sourceLocation: SourceLocation): Expression @@ -29,3 +32,13 @@ export function extractKey( }) } } + +export function assertCanBeUsedForStorage(ptype: PType, sourceLocation?: SourceLocation) { + if (ptype instanceof UnsupportedType || ptype instanceof TransientType) { + throw new CodeError(`Type ${ptype} cannot be used for storage`, { sourceLocation }) + } + if ((ptype instanceof MutableObjectPType || ptype instanceof ImmutableObjectPType) && ptype.runtimeOnly) { + const ptypeName = ptype.alias?.fullName || ptype.toString() + throw new CodeError(`Type ${ptypeName} cannot be used for storage`, { sourceLocation }) + } +} diff --git a/src/awst_build/models/app-storage-declaration.ts b/src/awst_build/models/app-storage-declaration.ts index 532987837..d1c5f70f3 100644 --- a/src/awst_build/models/app-storage-declaration.ts +++ b/src/awst_build/models/app-storage-declaration.ts @@ -2,10 +2,9 @@ import { nodeFactory } from '../../awst/node-factory' import type { AppStorageDefinition, BytesConstant } from '../../awst/nodes' import { AppStorageKind, BytesEncoding } from '../../awst/nodes' import type { SourceLocation } from '../../awst/source-location' -import { CodeError } from '../../errors' import { invariant, utf8ToUint8Array } from '../../util' import type { AppStorageType, ContractClassPType } from '../ptypes' -import { BoxMapPType, BoxPType, GlobalStateType, LocalStateType, TransientType, UnsupportedType } from '../ptypes' +import { BoxMapPType, BoxPType, GlobalStateType, LocalStateType } from '../ptypes' export class AppStorageDeclaration { readonly memberName: string @@ -56,9 +55,6 @@ export class AppStorageDeclaration { } get definition(): AppStorageDefinition { - if (this.ptype.contentType instanceof UnsupportedType || this.ptype.contentType instanceof TransientType) { - throw new CodeError(`Type ${this.ptype.contentType} cannot be used for storage`, { sourceLocation: this.sourceLocation }) - } return nodeFactory.appStorageDefinition({ ...this, kind: this.kind, diff --git a/src/awst_build/ptypes/arc4-types.ts b/src/awst_build/ptypes/arc4-types.ts index 3b123e62e..4360907c3 100644 --- a/src/awst_build/ptypes/arc4-types.ts +++ b/src/awst_build/ptypes/arc4-types.ts @@ -194,7 +194,7 @@ export class ARC4StructType extends ARC4EncodedType { } get nativeType(): MutableObjectPType { - return new MutableObjectPType({ properties: this.fields }) + return new MutableObjectPType({ properties: this.fields, runtimeOnly: false }) } get wtype(): wtypes.ARC4Struct { @@ -639,6 +639,7 @@ export class TypedApplicationCallResponseType extends ImmutableObjectPType { itxn: applicationItxnType, returnValue, }, + runtimeOnly: false, }) this.name = `TypedApplicationCallResponseType<${returnValue.name}>` this.returnValue = returnValue diff --git a/src/awst_build/ptypes/index.ts b/src/awst_build/ptypes/index.ts index 4601b6100..ae7c4afa7 100644 --- a/src/awst_build/ptypes/index.ts +++ b/src/awst_build/ptypes/index.ts @@ -224,12 +224,12 @@ export class IntersectionPType extends TransientType { module: 'lib.d.ts', singleton: false, typeMessage: transientTypeErrors.intersectionTypes(name).usedAsType, - expressionMessage: transientTypeErrors.unionTypes(name).usedInExpression, + expressionMessage: transientTypeErrors.intersectionTypes(name).usedInExpression, }) this.types = types } - static fromTypes(types: PType[]) { + static fromTypes(types: PType[], sourceLocation: SourceLocation, description?: string, alias?: SymbolName) { if (types.length === 0) { throw new InternalError('Cannot create intersection of zero types') } @@ -237,6 +237,12 @@ export class IntersectionPType extends TransientType { if (distinctTypes.length === 1) { return distinctTypes[0] } + if (types.every((p) => p instanceof ImmutableObjectPType)) { + return objectTypeFromIntersectionParts(ImmutableObjectPType, types, sourceLocation, description, alias) + } + if (types.every((p) => p instanceof MutableObjectPType)) { + return objectTypeFromIntersectionParts(MutableObjectPType, types, sourceLocation, description, alias) + } return new IntersectionPType({ types: distinctTypes, }) @@ -932,6 +938,7 @@ abstract class ObjectPType extends PType { readonly properties: Record readonly singleton = false readonly immutable: boolean + readonly runtimeOnly: boolean constructor(props: { alias?: SymbolName | null @@ -939,6 +946,7 @@ abstract class ObjectPType extends PType { description?: string namePrefix: string immutable: boolean + runtimeOnly: boolean }) { super() this.name = `${props.namePrefix}${generateObjectHash(props.properties)}` @@ -946,6 +954,7 @@ abstract class ObjectPType extends PType { this.description = props.description this.alias = props.alias ?? null this.immutable = props.immutable + this.runtimeOnly = props.runtimeOnly } orderedProperties() { @@ -953,7 +962,7 @@ abstract class ObjectPType extends PType { } toString(): string { - return `{${this.orderedProperties() + return `${this.runtimeOnly ? '@runtimeOnly' : ''}{${this.orderedProperties() .map((p) => `${this.immutable ? 'readonly ' : ''}${p[0]}:${p[1].name}`) .join(',')}}` } @@ -973,6 +982,7 @@ export class ObjectLiteralPType extends ObjectPType { ...props, namePrefix: `ObjectLiteral`, immutable: false, + runtimeOnly: false, }) } @@ -985,6 +995,7 @@ export class ObjectLiteralPType extends ObjectPType { alias: this.alias, properties: this.properties, description: this.description, + runtimeOnly: this.runtimeOnly, }) } getMutable(): MutableObjectPType { @@ -992,6 +1003,7 @@ export class ObjectLiteralPType extends ObjectPType { alias: this.alias, properties: this.properties, description: this.description, + runtimeOnly: this.runtimeOnly, }) } @@ -1016,7 +1028,7 @@ export class ObjectLiteralPType extends ObjectPType { export class ImmutableObjectPType extends ObjectPType { readonly [PType.IdSymbol] = 'ImmutableObjectPType' - constructor(props: { alias?: SymbolName | null; properties: Record; description?: string }) { + constructor(props: { alias?: SymbolName | null; properties: Record; description?: string; runtimeOnly: boolean }) { super({ ...props, namePrefix: `ReadonlyObject`, @@ -1049,7 +1061,7 @@ export class ImmutableObjectPType extends ObjectPType { export class MutableObjectPType extends ObjectPType { readonly [PType.IdSymbol] = 'MutableObjectPType' - constructor(props: { alias?: SymbolName | null; properties: Record; description?: string }) { + constructor(props: { alias?: SymbolName | null; properties: Record; description?: string; runtimeOnly: boolean }) { super({ ...props, namePrefix: `Object`, @@ -1071,6 +1083,7 @@ export class MutableObjectPType extends ObjectPType { alias: this.alias, properties: this.properties, description: this.description, + runtimeOnly: this.runtimeOnly, }) } @@ -1859,6 +1872,7 @@ export const compiledContractType = new ImmutableObjectPType({ localUints: uint64PType, localBytes: uint64PType, }, + runtimeOnly: false, }) export const compiledLogicSigType = new ImmutableObjectPType({ alias: new SymbolName({ @@ -1869,6 +1883,7 @@ export const compiledLogicSigType = new ImmutableObjectPType({ properties: { account: accountPType, }, + runtimeOnly: false, }) export const arc28EmitFunction = new LibFunctionType({ @@ -1967,3 +1982,36 @@ export const validateEncodingFunctionPType = new LibFunctionType({ name: 'validateEncoding', module: Constants.moduleNames.algoTs.util, }) + +function objectTypeFromIntersectionParts( + constructor: typeof ImmutableObjectPType | typeof MutableObjectPType, + types: ObjectPType[], + sourceLocation: SourceLocation, + description?: string, + alias?: SymbolName, +) { + const allProperties = new Map() + for (const type of types) { + for (const [propName, propType] of type.orderedProperties()) { + let propTypes = allProperties.get(propName) + if (propTypes === undefined) { + propTypes = [] + allProperties.set(propName, propTypes) + } + propTypes.push(propType) + } + } + + const properties: Record = {} + for (const [propName, propTypes] of allProperties.entries()) { + if (propName.startsWith('__@')) { + // Symbol property - ignore + // TODO: Check AST nodes to confirm? + continue + } + const ptype = propTypes.length === 1 ? propTypes[0] : IntersectionPType.fromTypes(propTypes, sourceLocation, undefined) + properties[propName] = ptype + } + + return new constructor({ alias, properties, description, runtimeOnly: true }) +} diff --git a/src/awst_build/ptypes/transient-type-errors.ts b/src/awst_build/ptypes/transient-type-errors.ts index bece2929d..c5f0ac0c6 100644 --- a/src/awst_build/ptypes/transient-type-errors.ts +++ b/src/awst_build/ptypes/transient-type-errors.ts @@ -13,8 +13,8 @@ export const transientTypeErrors = { usedInExpression: `Union types are only valid in boolean expressions. Expression type is ${typeName}`, }), intersectionTypes: (typeName) => ({ - usedAsType: `Intersection types are not valid as a variable, parameter, return, or property type. Expression type is ${typeName}`, - usedInExpression: `Intersection types not valid here. Expression type is ${typeName}`, + usedAsType: `Some intersection types are not valid as a variable, parameter, return, or property type. Expression type is ${typeName}`, + usedInExpression: `Some intersection types not valid here. Expression type is ${typeName}`, }), optionalFields: (typeName) => ({ usedAsType: `${typeName} type should not be used explicitly as it contains optional fields which cannot be interrogated at runtime. Either remove the type annotation or use \`EXPRESSION satisfies ${typeName}\``, diff --git a/src/awst_build/type-resolver.ts b/src/awst_build/type-resolver.ts index 363bc0395..2cbb31c76 100644 --- a/src/awst_build/type-resolver.ts +++ b/src/awst_build/type-resolver.ts @@ -254,7 +254,8 @@ export class TypeResolver { if (parts.some((p) => p.equals(arc4StructBaseType))) { return arc4StructBaseType } else { - return IntersectionPType.fromTypes(parts) + const alias = tsType.aliasSymbol ? this.getSymbolFullName(tsType.aliasSymbol, sourceLocation) : undefined + return IntersectionPType.fromTypes(parts, sourceLocation, tryGetTypeDescription(tsType), alias) } } @@ -381,9 +382,9 @@ export class TypeResolver { } } if (expectReadonly) { - return new ImmutableObjectPType({ alias: typeAlias, properties, description: tryGetTypeDescription(tsType) }) + return new ImmutableObjectPType({ alias: typeAlias, properties, description: tryGetTypeDescription(tsType), runtimeOnly: false }) } else { - return new MutableObjectPType({ alias: typeAlias, properties, description: tryGetTypeDescription(tsType) }) + return new MutableObjectPType({ alias: typeAlias, properties, description: tryGetTypeDescription(tsType), runtimeOnly: false }) } } @@ -621,7 +622,7 @@ function isReadonlyPropertySymbol(prop: ts.Symbol): boolean { } function tryGetTypeDescription(tsType: ts.Type): string | undefined { - const dec = tsType.aliasSymbol?.valueDeclaration ?? tsType.symbol.valueDeclaration + const dec = tsType.aliasSymbol?.valueDeclaration ?? tsType.symbol?.valueDeclaration if (!dec) return undefined const docs = ts.getJSDocCommentsAndTags(dec) for (const doc of docs) { diff --git a/src/util/index.ts b/src/util/index.ts index 242cd814d..7ccb81d2c 100644 --- a/src/util/index.ts +++ b/src/util/index.ts @@ -7,9 +7,9 @@ import { CodeError, InternalError } from '../errors' import type { DeliberateAny } from '../typescript-helpers' import type { AbsolutePath } from './absolute-path' +export { hexToUint8Array, uint8ArrayToHex } from './base-16' export { base32ToUint8Array, uint8ArrayToBase32 } from './base-32' export { base64ToUint8Array, uint8ArrayToBase64 } from './base-64' -export { hexToUint8Array, uint8ArrayToHex } from './base-16' class InvariantError extends InternalError {} diff --git a/tests/expected-output/intersection-types.algo.ts b/tests/expected-output/intersection-types.algo.ts index dd4bef5da..c7a493c68 100644 --- a/tests/expected-output/intersection-types.algo.ts +++ b/tests/expected-output/intersection-types.algo.ts @@ -1,21 +1,64 @@ -import { type Account, Contract, Txn } from '@algorandfoundation/algorand-typescript' +import { type Account, Box, BoxMap, Contract, emit, GlobalState, LocalState, Txn } from '@algorandfoundation/algorand-typescript' import { Uint32 } from '@algorandfoundation/algorand-typescript/arc4' -type MyAccount = { - account: Account -} - +type WithAccount = { account: Account } type WithId = { id: Uint32 } -type MyAccountWithId = MyAccount & WithId +type IntersectionType = WithAccount & WithId export class HelloWorld extends Contract { - // @expect-error {account:Account} & {id:Uint<32>} cannot be used as an ABI return type - hello(name: string): MyAccountWithId { - const account: MyAccountWithId = { + // @expect-error Type tests/expected-output/intersection-types.algo.ts::IntersectionType cannot be used for storage + globalVar = GlobalState() + // @expect-error Type tests/expected-output/intersection-types.algo.ts::IntersectionType cannot be used for storage + localVar = LocalState() + // @expect-error Type tests/expected-output/intersection-types.algo.ts::IntersectionType cannot be used for storage + boxVar = Box({ key: 'box' }) + // @expect-error Type tests/expected-output/intersection-types.algo.ts::IntersectionType cannot be used for storage + boxMapVar = BoxMap({ keyPrefix: 'map' }) + // @expect-error @runtimeOnly{account:Account,id:Uint<32>} cannot be used as an ABI return type + cannotUseAsReturn(): IntersectionType { + const account: IntersectionType = { account: Txn.sender, id: new Uint32(13), } return account } + // @expect-error @runtimeOnly{account:Account,id:Uint<32>} cannot be used as an ABI param type + cannotUseAsParameter(param: IntersectionType): void {} + cannotBeLogged(): void { + const account: IntersectionType = { + account: Txn.sender, + id: new Uint32(13), + } + // @expect-error @runtimeOnly{account:Account,id:Uint<32>} cannot be encoded to an ARC4 type + emit(account) + } + cannotStoreToAGlobal() { + // @expect-error Type tests/expected-output/intersection-types.algo.ts::IntersectionType cannot be used for storage + GlobalState({ key: 'globalVar' }).value = { + account: Txn.sender, + id: new Uint32(13), + } + } + cannotStoreToALocal() { + // @expect-error Type tests/expected-output/intersection-types.algo.ts::IntersectionType cannot be used for storage + LocalState({ key: 'localVar' })(Txn.sender).value = { + account: Txn.sender, + id: new Uint32(13), + } + } + cannotStoreToABox() { + // @expect-error Type tests/expected-output/intersection-types.algo.ts::IntersectionType cannot be used for storage + Box({ key: 'box' }).value = { + account: Txn.sender, + id: new Uint32(13), + } + } + cannotStoreToABoxMap() { + // @expect-error Type tests/expected-output/intersection-types.algo.ts::IntersectionType cannot be used for storage + BoxMap({ keyPrefix: 'map' })(Txn.sender).value = { + account: Txn.sender, + id: new Uint32(13), + } + } }