or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

async-programming.mdcharacter-operations.mdcollections.mdconfiguration.mdcore-infrastructure.mddata-encoding.mddate-time.mdexternal-integration.mdindex.mdnumeric-types.mdreactive-programming.mdstring-operations.mdtype-system.md
tile.json

type-system.mddocs/

Type System

Runtime type information system providing comprehensive reflection capabilities for F# types with full type construction, analysis, and introspection support.

Reflection Module - Type Information

Runtime type information system for F# types, enabling type construction, analysis, and reflection capabilities for dynamic programming scenarios.

Core Type Definitions

// Field information as name-type pair
type FieldInfo = [string, TypeInfo];

// Constructor function type
type Constructor = Function;

// Union case information input
type CaseInfoInput = [number, string, TypeInfo[]?];

CaseInfo Class

Information about discriminated union cases.

class CaseInfo {
    constructor(declaringType: TypeInfo, tag: number, name: string, fields?: TypeInfo[]);
    
    // Properties
    declaringType: TypeInfo;  // The union type that declares this case
    tag: number;              // Numeric tag for the case
    name: string;             // Case name
    fields?: TypeInfo[];      // Field types for the case
}

// Usage
import { CaseInfo, TypeInfo } from "fable-library/Reflection.js";

// Example: Option<T> union case info
const someCase = new CaseInfo(
    optionTypeInfo,  // Declaring type
    0,               // Tag for Some case
    "Some",          // Case name
    [genericT]       // Field types: [T]
);

const noneCase = new CaseInfo(
    optionTypeInfo,  // Declaring type
    1,               // Tag for None case
    "None",          // Case name
    []               // No fields
);

TypeInfo Class

Comprehensive type information container.

class TypeInfo {
    constructor(
        fullname: string, 
        generics?: TypeInfo[], 
        constructor?: Constructor, 
        fields?: () => FieldInfo[], 
        cases?: () => CaseInfo[]
    );
    
    // Properties
    fullname: string;                    // Full type name
    generics?: TypeInfo[];               // Generic type arguments
    constructor?: Constructor;           // Type constructor function
    fields?: () => FieldInfo[];          // Record/class fields (lazy)
    cases?: () => CaseInfo[];           // Union cases (lazy)
    
    // Methods
    toString(): string;
    Equals(other: TypeInfo): boolean;
    CompareTo(other: TypeInfo): number;
}

// Usage
import { TypeInfo } from "fable-library/Reflection.js";

// Create type info for a simple type
const stringType = new TypeInfo("System.String");

// Create generic type info
const listOfInt = new TypeInfo(
    "Microsoft.FSharp.Collections.List`1",
    [new TypeInfo("System.Int32")]
);

console.log(stringType.toString()); // "System.String"
console.log(listOfInt.toString());  // "Microsoft.FSharp.Collections.List`1[System.Int32]"

Type Construction Functions

Basic Type Construction

// Create simple type
function type(fullname: string, generics?: TypeInfo[]): TypeInfo;

// Create record type
function record(
    fullname: string, 
    generics: TypeInfo[], 
    constructor: Constructor, 
    fields: () => FieldInfo[]
): TypeInfo;

// Create anonymous record type
function anonRecord(...fields: FieldInfo[]): TypeInfo;

// Usage
import { type, record, anonRecord } from "fable-library/Reflection.js";

// Simple types
const intType = type("System.Int32");
const stringType = type("System.String");

// Generic type
const listOfString = type("System.Collections.Generic.List`1", [stringType]);

// Record type
const personType = record(
    "MyApp.Person",
    [],
    PersonConstructor,
    () => [
        ["name", stringType],
        ["age", intType]
    ]
);

// Anonymous record
const coordType = anonRecord(
    ["x", intType],
    ["y", intType]
);

console.log(personType.fullname); // "MyApp.Person"
console.log(coordType.fullname);  // Anonymous record type name

Union Type Construction

// Create discriminated union type
function union(
    fullname: string, 
    generics: TypeInfo[], 
    constructor: Constructor, 
    cases: () => CaseInfoInput[]
): TypeInfo;

// Usage
import { union, type } from "fable-library/Reflection.js";

// Define Option<T> union type
const optionType = union(
    "Microsoft.FSharp.Core.Option`1",
    [type("T")], // Generic parameter
    OptionConstructor,
    () => [
        [0, "None", []],           // Case tag 0, no fields
        [1, "Some", [type("T")]]   // Case tag 1, one field of type T
    ]
);

// Define Result<T, E> union type
const resultType = union(
    "Microsoft.FSharp.Core.Result`2",
    [type("T"), type("E")],
    ResultConstructor,
    () => [
        [0, "Ok", [type("T")]],
        [1, "Error", [type("E")]]
    ]
);

Collection and Function Types

// Create tuple type
function tuple(...generics: TypeInfo[]): TypeInfo;

// Create delegate type
function delegate(...generics: TypeInfo[]): TypeInfo;

// Create function type
function lambda(argument: TypeInfo, returnType: TypeInfo): TypeInfo;

// Create option type
function option(generic: TypeInfo): TypeInfo;

// Create list type
function list(generic: TypeInfo): TypeInfo;

// Create array type
function array(generic: TypeInfo): TypeInfo;

// Usage
import { 
    tuple, delegate, lambda, option, 
    list, array, type 
} from "fable-library/Reflection.js";

const intType = type("System.Int32");
const stringType = type("System.String");
const boolType = type("System.Boolean");

// Tuple types
const coordTuple = tuple(intType, intType);                    // (int * int)
const tripleTuple = tuple(stringType, intType, boolType);     // (string * int * bool)

// Function types
const intToString = lambda(intType, stringType);              // int -> string
const stringPredicate = lambda(stringType, boolType);        // string -> bool

// Collection types
const intOption = option(intType);                            // int option
const stringList = list(stringType);                         // string list
const intArray = array(intType);                             // int[]

// Delegate types (for .NET compatibility)
const actionDelegate = delegate(stringType);                  // Action<string>
const funcDelegate = delegate(intType, stringType);          // Func<int, string>

console.log(coordTuple.fullname);     // Tuple type name
console.log(intToString.fullname);    // Function type name
console.log(intOption.fullname);      // "Microsoft.FSharp.Core.Option`1"

Type Analysis Functions

Generic Type Operations

// Get generic arguments from type
function getGenerics(t: TypeInfo): TypeInfo[];

// Compare types
function equals(t1: TypeInfo, t2: TypeInfo): boolean;
function compare(t1: TypeInfo, t2: TypeInfo): number;

// Usage
import { getGenerics, equals, compare, list, type } from "fable-library/Reflection.js";

const stringType = type("System.String");
const intType = type("System.Int32");
const stringList = list(stringType);
const intList = list(intType);

// Get generics
const listGenerics = getGenerics(stringList);
console.log(listGenerics.length); // 1
console.log(listGenerics[0].fullname); // "System.String"

// Type comparison
console.log(equals(stringType, stringType)); // true
console.log(equals(stringList, intList)); // false

const comparison = compare(stringType, intType);
console.log(comparison); // Lexicographic comparison result

Type Name Operations

// Get type name components
function fullName(t: TypeInfo): string;
function namespace(t: TypeInfo): string;
function name(t: TypeInfo): string;

// Usage
import { fullName, namespace, name, type } from "fable-library/Reflection.js";

const listType = type("System.Collections.Generic.List`1", [type("System.String")]);

console.log(fullName(listType));  // "System.Collections.Generic.List`1[System.String]"
console.log(namespace(listType)); // "System.Collections.Generic"
console.log(name(listType));      // "List`1"

// Custom type
const personType = type("MyApp.Domain.Person");
console.log(fullName(personType));  // "MyApp.Domain.Person"
console.log(namespace(personType)); // "MyApp.Domain"
console.log(name(personType));      // "Person"

Type Decomposition Functions

Record Type Analysis

// Get record field information
function getRecordElements(t: TypeInfo): FieldInfo[];

// Usage
import { getRecordElements, record, type } from "fable-library/Reflection.js";

// Define a record type
const personType = record(
    "MyApp.Person",
    [],
    PersonConstructor,
    () => [
        ["firstName", type("System.String")],
        ["lastName", type("System.String")],
        ["age", type("System.Int32")],
        ["email", type("System.String")]
    ]
);

// Analyze record structure
const fields = getRecordElements(personType);
console.log("Person record fields:");
fields.forEach(([fieldName, fieldType]) => {
    console.log(`  ${fieldName}: ${fieldType.fullname}`);
});
// Output:
//   firstName: System.String
//   lastName: System.String  
//   age: System.Int32
//   email: System.String

Union Type Analysis

// Get union case information
function getUnionCases(t: TypeInfo): CaseInfo[];

// Usage
import { getUnionCases, union, type } from "fable-library/Reflection.js";

// Define a union type
const shapeType = union(
    "MyApp.Shape",
    [],
    ShapeConstructor,
    () => [
        [0, "Circle", [type("System.Double")]],                    // radius
        [1, "Rectangle", [type("System.Double"), type("System.Double")]], // width, height
        [2, "Triangle", [type("System.Double"), type("System.Double"), type("System.Double")]] // sides
    ]
);

// Analyze union structure
const cases = getUnionCases(shapeType);
console.log("Shape union cases:");
cases.forEach(caseInfo => {
    const fieldTypes = caseInfo.fields?.map(f => f.fullname).join(", ") || "none";
    console.log(`  ${caseInfo.name} (tag ${caseInfo.tag}): ${fieldTypes}`);
});
// Output:
//   Circle (tag 0): System.Double
//   Rectangle (tag 1): System.Double, System.Double
//   Triangle (tag 2): System.Double, System.Double, System.Double

Tuple and Function Analysis

// Get tuple element types
function getTupleElements(t: TypeInfo): TypeInfo[];

// Get function argument and return types
function getFunctionElements(t: TypeInfo): TypeInfo[];

// Usage
import { getTupleElements, getFunctionElements, tuple, lambda, type } from "fable-library/Reflection.js";

const stringType = type("System.String");
const intType = type("System.Int32");
const boolType = type("System.Boolean");

// Tuple analysis
const coordTuple = tuple(intType, intType, stringType);
const tupleElements = getTupleElements(coordTuple);
console.log("Tuple elements:");
tupleElements.forEach((elementType, index) => {
    console.log(`  [${index}]: ${elementType.fullname}`);
});

// Function analysis
const parseFunction = lambda(stringType, intType); // string -> int
const functionElements = getFunctionElements(parseFunction);
console.log("Function signature:");
console.log(`  Argument: ${functionElements[0].fullname}`);
console.log(`  Return: ${functionElements[1].fullname}`);

Generic Type Construction

// Get generic arguments from existing type
function getGenericArguments(t: TypeInfo): TypeInfo[];

// Create new generic instances
function makeRecordType(typeDef: TypeInfo, generics: TypeInfo[]): TypeInfo;
function makeUnionType(typeDef: TypeInfo, generics: TypeInfo[]): TypeInfo;
function makeGenericType(typeDef: TypeInfo, generics: TypeInfo[]): TypeInfo;

// Usage
import { 
    getGenericArguments, makeGenericType, 
    type, list 
} from "fable-library/Reflection.js";

// Start with generic type definition
const listTypeDef = type("System.Collections.Generic.List`1"); // Open generic

// Create specific instances
const stringType = type("System.String");
const intType = type("System.Int32");

const stringList = makeGenericType(listTypeDef, [stringType]);
const intList = makeGenericType(listTypeDef, [intType]);

console.log(stringList.fullname); // "System.Collections.Generic.List`1[System.String]"
console.log(intList.fullname);    // "System.Collections.Generic.List`1[System.Int32]"

// Analyze existing generic type
const existingGenerics = getGenericArguments(stringList);
console.log(existingGenerics[0].fullname); // "System.String"

Built-in Type Constants

// Primitive type constants
const obj_type: TypeInfo;       // System.Object
const unit_type: TypeInfo;      // Microsoft.FSharp.Core.Unit
const char_type: TypeInfo;      // System.Char
const string_type: TypeInfo;    // System.String
const bool_type: TypeInfo;      // System.Boolean

// Integer types
const int8_type: TypeInfo;      // System.SByte
const uint8_type: TypeInfo;     // System.Byte
const int16_type: TypeInfo;     // System.Int16
const uint16_type: TypeInfo;    // System.UInt16
const int32_type: TypeInfo;     // System.Int32
const uint32_type: TypeInfo;    // System.UInt32
const int64_type: TypeInfo;     // System.Int64
const uint64_type: TypeInfo;    // System.UInt64

// Native integer types
const nativeint_type: TypeInfo;    // System.IntPtr
const unativeint_type: TypeInfo;   // System.UIntPtr

// Floating point types
const float32_type: TypeInfo;   // System.Single
const float64_type: TypeInfo;   // System.Double
const decimal_type: TypeInfo;   // System.Decimal

// Usage
import { 
    string_type, int32_type, bool_type, 
    float64_type, list, option 
} from "fable-library/Reflection.js";

// Use built-in types for construction
const stringList = list(string_type);
const intOption = option(int32_type);
const boolArray = array(bool_type);

console.log("Built-in types:");
console.log(`String: ${string_type.fullname}`);
console.log(`Int32: ${int32_type.fullname}`);
console.log(`Boolean: ${bool_type.fullname}`);
console.log(`Double: ${float64_type.fullname}`);

// Create complex types using built-ins
const personRecord = record(
    "Person",
    [],
    PersonConstructor,
    () => [
        ["name", string_type],
        ["age", int32_type],
        ["height", float64_type],
        ["isActive", bool_type]
    ]
);

Advanced Reflection Examples

Dynamic Type Construction

import { 
    type, record, union, tuple, lambda,
    string_type, int32_type, bool_type
} from "fable-library/Reflection.js";

// Build types dynamically based on configuration
interface TypeConfig {
    kind: 'record' | 'union' | 'tuple' | 'function';
    name?: string;
    fields?: Array<{name: string, type: string}>;
    cases?: Array<{name: string, fields: string[]}>;
    elements?: string[];
    signature?: {arg: string, return: string};
}

function buildTypeFromConfig(config: TypeConfig): TypeInfo {
    const typeMap = new Map([
        ['string', string_type],
        ['int', int32_type],
        ['bool', bool_type]
    ]);
    
    const getType = (typeName: string): TypeInfo => {
        return typeMap.get(typeName) || type(typeName);
    };
    
    switch (config.kind) {
        case 'record':
            if (!config.name || !config.fields) {
                throw new Error('Record type requires name and fields');
            }
            return record(
                config.name,
                [],
                function() {},
                () => config.fields!.map(f => [f.name, getType(f.type)] as FieldInfo)
            );
            
        case 'union':
            if (!config.name || !config.cases) {
                throw new Error('Union type requires name and cases');
            }
            return union(
                config.name,
                [],
                function() {},
                () => config.cases!.map((c, i) => [
                    i,
                    c.name,
                    c.fields.map(getType)
                ] as CaseInfoInput)
            );
            
        case 'tuple':
            if (!config.elements) {
                throw new Error('Tuple type requires elements');
            }
            return tuple(...config.elements.map(getType));
            
        case 'function':
            if (!config.signature) {
                throw new Error('Function type requires signature');
            }
            return lambda(
                getType(config.signature.arg),
                getType(config.signature.return)
            );
            
        default:
            throw new Error(`Unknown type kind: ${config.kind}`);
    }
}

// Usage examples
const userRecordConfig: TypeConfig = {
    kind: 'record',
    name: 'User',
    fields: [
        {name: 'id', type: 'int'},
        {name: 'username', type: 'string'},
        {name: 'isActive', type: 'bool'}
    ]
};

const resultUnionConfig: TypeConfig = {
    kind: 'union',
    name: 'ValidationResult',
    cases: [
        {name: 'Success', fields: ['string']},
        {name: 'Error', fields: ['string']}
    ]
};

const coordinateConfig: TypeConfig = {
    kind: 'tuple',
    elements: ['int', 'int']
};

const validatorConfig: TypeConfig = {
    kind: 'function',
    signature: {arg: 'string', return: 'bool'}
};

// Build types
const userType = buildTypeFromConfig(userRecordConfig);
const resultType = buildTypeFromConfig(resultUnionConfig);
const coordType = buildTypeFromConfig(coordinateConfig);
const validatorType = buildTypeFromConfig(validatorConfig);

console.log("Dynamically created types:");
console.log(`User record: ${userType.fullname}`);
console.log(`Result union: ${resultType.fullname}`);
console.log(`Coordinate tuple: ${coordType.fullname}`);
console.log(`Validator function: ${validatorType.fullname}`);

Runtime Type Analysis and Validation

import { 
    getRecordElements, getUnionCases, getTupleElements,
    getFunctionElements, equals, type
} from "fable-library/Reflection.js";

// Runtime type analyzer
class TypeAnalyzer {
    static analyzeType(typeInfo: TypeInfo): any {
        const analysis = {
            fullName: typeInfo.fullname,
            kind: 'unknown' as string,
            details: {} as any
        };
        
        // Check if it's a record
        try {
            const fields = getRecordElements(typeInfo);
            analysis.kind = 'record';
            analysis.details.fields = fields.map(([name, type]) => ({
                name,
                type: type.fullname
            }));
            return analysis;
        } catch (e) {
            // Not a record, continue checking
        }
        
        // Check if it's a union
        try {
            const cases = getUnionCases(typeInfo);
            analysis.kind = 'union';
            analysis.details.cases = cases.map(c => ({
                tag: c.tag,
                name: c.name,
                fields: c.fields?.map(f => f.fullname) || []
            }));
            return analysis;
        } catch (e) {
            // Not a union, continue checking
        }
        
        // Check if it's a tuple
        try {
            const elements = getTupleElements(typeInfo);
            analysis.kind = 'tuple';
            analysis.details.elements = elements.map(e => e.fullname);
            return analysis;
        } catch (e) {
            // Not a tuple, continue checking
        }
        
        // Check if it's a function
        try {
            const funcElements = getFunctionElements(typeInfo);
            analysis.kind = 'function';
            analysis.details.argument = funcElements[0].fullname;
            analysis.details.returnType = funcElements[1].fullname;
            return analysis;
        } catch (e) {
            // Not a function, it's a simple type
        }
        
        analysis.kind = 'simple';
        return analysis;
    }
    
    static validateTypeCompatibility(expected: TypeInfo, actual: TypeInfo): boolean {
        return equals(expected, actual);
    }
    
    static getTypeSignature(typeInfo: TypeInfo): string {
        const analysis = this.analyzeType(typeInfo);
        
        switch (analysis.kind) {
            case 'record':
                const recordFields = analysis.details.fields
                    .map((f: any) => `${f.name}: ${f.type}`)
                    .join('; ');
                return `{ ${recordFields} }`;
                
            case 'union':
                const unionCases = analysis.details.cases
                    .map((c: any) => {
                        const fields = c.fields.length > 0 ? ` of ${c.fields.join(' * ')}` : '';
                        return `${c.name}${fields}`;
                    })
                    .join(' | ');
                return unionCases;
                
            case 'tuple':
                return analysis.details.elements.join(' * ');
                
            case 'function':
                return `${analysis.details.argument} -> ${analysis.details.returnType}`;
                
            default:
                return analysis.fullName;
        }
    }
}

// Usage
const testTypes = [userType, resultType, coordType, validatorType];

testTypes.forEach(t => {
    const analysis = TypeAnalyzer.analyzeType(t);
    const signature = TypeAnalyzer.getTypeSignature(t);
    
    console.log(`\nType: ${t.fullname}`);
    console.log(`Kind: ${analysis.kind}`);
    console.log(`Signature: ${signature}`);
    console.log(`Details:`, JSON.stringify(analysis.details, null, 2));
});