Marjoram now provides full TypeScript support for array methods on schema properties, eliminating the need to cast to any when using array helper methods.
When you create a view model with array properties, you get full IntelliSense and type safety for array methods:
import { useViewModel } from 'marjoram';
const viewModel = useViewModel({
items: ['apple', 'banana', 'cherry'],
numbers: [1, 2, 3, 4, 5],
todos: [
{ id: 1, text: 'Learn TypeScript', done: false },
{ id: 2, text: 'Build awesome app', done: true }
]
});
// ✅ Full type safety - no casting needed!
const uppercaseItems = viewModel.$items.map(item => item.toUpperCase());
const evenNumbers = viewModel.$numbers.filter(num => num % 2 === 0);
const foundItem = viewModel.$items.find(item => item.includes('ban'));
const totalCount = viewModel.$items.length;
// ✅ Additional array methods now available
const hasApple = viewModel.$items.includes('apple');
const appleIndex = viewModel.$items.indexOf('apple');
const itemList = viewModel.$items.join(', ');
const firstTwo = viewModel.$items.slice(0, 2);
const hasAnyLongNames = viewModel.$items.some(item => item.length > 5);
const allValidItems = viewModel.$items.every(item => item.length > 0);
// ✅ Works in templates too
const view = html`
<ul>
${viewModel.$todos.map(todo => html`
<li class="${todo.done ? 'completed' : ''}">
${todo.text}
</li>
`)}
</ul>
`;
Schema array properties provide the following methods with full type support:
map<U>(callback: (value: T, index: number, array: T[]) => U): U[]filter(predicate: (value: T, index: number, array: T[]) => boolean): T[]forEach(callback: (value: T, index: number, array: T[]) => void): voidfind(predicate: (value: T, index: number, array: T[]) => boolean): T | undefinedreduce<U>(callback: (prev: U, current: T, index: number, array: T[]) => U, initialValue: U): Uincludes(searchElement: T, fromIndex?: number): booleanindexOf(searchElement: T, fromIndex?: number): numberfindIndex(predicate: (value: T, index: number, array: T[]) => boolean): numbersome(predicate: (value: T, index: number, array: T[]) => boolean): booleanevery(predicate: (value: T, index: number, array: T[]) => boolean): booleanslice(start?: number, end?: number): T[]concat(...items: ConcatArray<T>[]): T[]join(separator?: string): stringlength: number | undefinedThe library automatically detects array properties and provides the appropriate typing:
import { TypedSchemaProp, SchemaArrayProp } from 'marjoram';
// For arrays: SchemaArrayProp<T>
type StringArrayProp = TypedSchemaProp<string[]>; // SchemaArrayProp<string>
// For non-arrays: SchemaProp
type StringProp = TypedSchemaProp<string>; // SchemaProp
Before (required casting):
// ❌ Had to cast to any
const mapped = (viewModel.$items as any).map(item => item.toUpperCase());
const length = (viewModel.$items as any).length;
After (full type safety):
// ✅ Full IntelliSense and type checking
const mapped = viewModel.$items.map(item => item.toUpperCase());
const length = viewModel.$items.length;
You can also use typed properties when working directly with schemas:
import { useSchema } from 'marjoram';
const schema = useSchema();
const arrayProp = schema.defineProperty(['x', 'y', 'z']); // SchemaArrayProp<string>
const stringProp = schema.defineProperty('hello'); // SchemaProp
// Type-safe array operations
const mapped = arrayProp.map(item => item.toUpperCase()); // string[]
const filtered = arrayProp.filter(item => item > 'x'); // string[]
// Regular schema prop operations
stringProp.update('world');
console.log(stringProp.value); // 'world'
This enhancement provides a much better developer experience with full IntelliSense support while maintaining backward compatibility with existing code.