Runtime type information system providing comprehensive reflection capabilities for F# types with full type construction, analysis, and introspection support.
Runtime type information system for F# types, enabling type construction, analysis, and reflection capabilities for dynamic programming scenarios.
// 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[]?];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
);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]"// 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// 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")]]
]
);// 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"// 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// 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"// 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// 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// 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}`);// 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"// 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]
]
);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}`);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));
});