use typescript_ir::{BinaryOp, Expression, PrimitiveType, Program, Statement, TypeAnnotation, UnaryOp};
use typescript_types::TsError;
struct TypeEnvironment {
variables: std::collections::HashMap<String, TypeAnnotation>,
functions: std::collections::HashMap<String, (Vec<TypeAnnotation>, Option<TypeAnnotation>)>,
type_aliases: std::collections::HashMap<String, TypeAnnotation>,
interfaces: std::collections::HashMap<String, Vec<(String, TypeAnnotation, bool)>>,
}
impl Clone for TypeEnvironment {
fn clone(&self) -> Self {
Self {
variables: self.variables.clone(),
functions: self.functions.clone(),
type_aliases: self.type_aliases.clone(),
interfaces: self.interfaces.clone(),
}
}
}
impl TypeEnvironment {
fn new() -> Self {
Self {
variables: std::collections::HashMap::new(),
functions: std::collections::HashMap::new(),
type_aliases: std::collections::HashMap::new(),
interfaces: std::collections::HashMap::new(),
}
}
fn add_variable(&mut self, name: String, ty: TypeAnnotation) {
self.variables.insert(name, ty);
}
fn add_function(&mut self, name: String, params: Vec<TypeAnnotation>, return_type: Option<TypeAnnotation>) {
self.functions.insert(name, (params, return_type));
}
fn add_type_alias(&mut self, name: String, ty: TypeAnnotation) {
self.type_aliases.insert(name, ty);
}
fn add_interface(&mut self, name: String, members: Vec<(String, TypeAnnotation, bool)>) {
self.interfaces.insert(name, members);
}
fn get_variable(&self, name: &str) -> Option<&TypeAnnotation> {
self.variables.get(name)
}
fn get_function(&self, name: &str) -> Option<&(Vec<TypeAnnotation>, Option<TypeAnnotation>)> {
self.functions.get(name)
}
fn get_type_alias(&self, name: &str) -> Option<&TypeAnnotation> {
self.type_aliases.get(name)
}
fn get_interface(&self, name: &str) -> Option<&Vec<(String, TypeAnnotation, bool)>> {
self.interfaces.get(name)
}
fn is_compatible(&self, from: &TypeAnnotation, to: &TypeAnnotation) -> bool {
is_compatible(self, from, to)
}
fn resolve_type(&self, ty: &TypeAnnotation) -> TypeAnnotation {
match ty {
TypeAnnotation::TypeReference(name) => {
if let Some(alias) = self.get_type_alias(name) {
self.resolve_type(alias)
}
else {
ty.clone()
}
}
TypeAnnotation::Array(elem) => {
let resolved_elem = self.resolve_type(elem);
TypeAnnotation::Array(Box::new(resolved_elem))
}
TypeAnnotation::Object(members) => {
let resolved_members: Vec<(String, TypeAnnotation)> =
members.iter().map(|(name, ty)| (name.clone(), self.resolve_type(ty))).collect();
TypeAnnotation::Object(resolved_members)
}
TypeAnnotation::Union(types) => {
let resolved_types: Vec<TypeAnnotation> = types.iter().map(|ty| self.resolve_type(ty)).collect();
TypeAnnotation::Union(resolved_types)
}
TypeAnnotation::Intersection(types) => {
let resolved_types: Vec<TypeAnnotation> = types.iter().map(|ty| self.resolve_type(ty)).collect();
TypeAnnotation::Intersection(resolved_types)
}
TypeAnnotation::Function { params, return_type } => {
let resolved_params: Vec<TypeAnnotation> = params.iter().map(|ty| self.resolve_type(ty)).collect();
let resolved_return = self.resolve_type(return_type);
TypeAnnotation::Function { params: resolved_params, return_type: Box::new(resolved_return) }
}
TypeAnnotation::Generic { name, args } => {
let resolved_args: Vec<TypeAnnotation> = args.iter().map(|ty| self.resolve_type(ty)).collect();
TypeAnnotation::Generic { name: name.clone(), args: resolved_args }
}
TypeAnnotation::Tuple(types) => {
let resolved_types: Vec<TypeAnnotation> = types.iter().map(|ty| self.resolve_type(ty)).collect();
TypeAnnotation::Tuple(resolved_types)
}
_ => ty.clone(),
}
}
}
fn is_compatible(env: &TypeEnvironment, from: &TypeAnnotation, to: &TypeAnnotation) -> bool {
let from = env.resolve_type(from);
let to = env.resolve_type(to);
match (&from, &to) {
(_, TypeAnnotation::Any) => true,
(TypeAnnotation::Any, _) => false,
(TypeAnnotation::Unknown, _) => true,
(_, TypeAnnotation::Unknown) => matches!(&from, TypeAnnotation::Unknown | TypeAnnotation::Any),
(TypeAnnotation::Never, _) => true,
(_, TypeAnnotation::Never) => matches!(&from, TypeAnnotation::Never),
(TypeAnnotation::Void, _) => matches!(&to, TypeAnnotation::Void | TypeAnnotation::Any | TypeAnnotation::Unknown),
(_, TypeAnnotation::Void) => {
matches!(&from, TypeAnnotation::Void | TypeAnnotation::Any | TypeAnnotation::Unknown | TypeAnnotation::Never)
}
(TypeAnnotation::Primitive(a), TypeAnnotation::Primitive(b)) => {
a == b
|| (*a == PrimitiveType::Null && *b == PrimitiveType::Undefined)
|| (*a == PrimitiveType::Undefined && *b == PrimitiveType::Null)
}
(TypeAnnotation::Array(a), TypeAnnotation::Array(b)) => is_compatible(env, a, b),
(TypeAnnotation::Object(from_members), TypeAnnotation::Object(to_members)) => {
let from_map: std::collections::HashMap<_, _> = from_members.iter().map(|(name, ty)| (name, ty)).collect();
to_members
.iter()
.all(|(name, ty)| from_map.get(name).map(|from_ty| is_compatible(env, from_ty, ty)).unwrap_or(false))
}
(_, TypeAnnotation::Union(to_types)) => to_types.iter().any(|to_ty| is_compatible(env, &from, to_ty)),
(_, TypeAnnotation::Intersection(to_types)) => to_types.iter().all(|to_ty| is_compatible(env, &from, to_ty)),
(
TypeAnnotation::Function { params: from_params, return_type: from_return },
TypeAnnotation::Function { params: to_params, return_type: to_return },
) => {
if from_params.len() != to_params.len() {
return false;
}
let params_compatible =
from_params.iter().zip(to_params.iter()).all(|(from_param, to_param)| is_compatible(env, to_param, from_param));
let return_compatible = is_compatible(env, from_return, to_return);
params_compatible && return_compatible
}
(TypeAnnotation::Generic { name: name1, args: args1 }, TypeAnnotation::Generic { name: name2, args: args2 }) => {
name1 == name2
&& args1.len() == args2.len()
&& args1.iter().zip(args2.iter()).all(|(a, b)| is_compatible(env, a, b))
}
(TypeAnnotation::Tuple(from_types), TypeAnnotation::Tuple(to_types)) => {
from_types.len() == to_types.len() && from_types.iter().zip(to_types.iter()).all(|(a, b)| is_compatible(env, a, b))
}
(TypeAnnotation::TypeReference(from_name), TypeAnnotation::TypeReference(to_name)) => from_name == to_name,
_ => false,
}
}
pub fn check(program: Program) -> Result<Program, TsError> {
let mut env = TypeEnvironment::new();
env.add_variable("console".to_string(), TypeAnnotation::Any);
for statement in &program.statements {
check_statement(statement, &mut env)?;
}
Ok(program)
}
fn check_statement(statement: &Statement, env: &mut TypeEnvironment) -> Result<(), TsError> {
match statement {
Statement::VariableDeclaration { name, ty, initializer } => {
if let Some(init) = initializer {
let init_type = check_expression(init, env)?;
if let Some(ty) = ty {
if !env.is_compatible(&init_type, ty) {
return Err(TsError::TypeError(format!(
"Type '{}' is not assignable to type '{}'",
format_type(&init_type),
format_type(ty)
)));
}
env.add_variable(name.clone(), ty.clone());
}
else {
env.add_variable(name.clone(), init_type);
}
}
else if let Some(ty) = ty {
env.add_variable(name.clone(), ty.clone());
}
else {
env.add_variable(name.clone(), TypeAnnotation::Any);
}
}
Statement::FunctionDeclaration { name, params, return_type, body, .. } => {
let mut func_env = env.clone();
let mut param_types = vec![];
for param in params {
func_env.add_variable(param.clone(), TypeAnnotation::Any);
param_types.push(TypeAnnotation::Any);
}
let mut return_types = vec![];
for stmt in body {
if let Statement::Return(Some(expr)) = stmt {
let return_type = check_expression(expr, &mut func_env)?;
return_types.push(return_type);
}
else {
check_statement(stmt, &mut func_env)?;
}
}
let inferred_return_type =
if return_types.is_empty() { TypeAnnotation::Void } else { infer_common_type(&func_env, &return_types) };
let final_return_type = if let Some(return_type) = return_type {
if !func_env.is_compatible(&inferred_return_type, return_type) {
return Err(TsError::TypeError(format!(
"Return type '{}' is not compatible with declared return type '{}'",
format_type(&inferred_return_type),
format_type(return_type)
)));
}
return_type.clone()
}
else {
inferred_return_type
};
env.add_function(name.clone(), param_types, Some(final_return_type));
}
Statement::ClassDeclaration { name, super_class, methods, .. } => {
let mut class_methods = std::collections::HashMap::new();
for method in methods {
let mut method_env = env.clone();
for param in &method.params {
method_env.add_variable(param.clone(), TypeAnnotation::Any);
}
let mut return_types = vec![];
for stmt in &method.body {
if let Statement::Return(Some(expr)) = stmt {
let return_type = check_expression(expr, &mut method_env)?;
return_types.push(return_type);
}
else {
check_statement(stmt, &mut method_env)?;
}
}
let return_type =
if return_types.is_empty() { TypeAnnotation::Void } else { infer_common_type(&method_env, &return_types) };
class_methods.insert(method.name.clone(), return_type);
}
let _ = class_methods;
}
Statement::InterfaceDeclaration { name, extends, members, .. } => {
let mut interface_members = vec![];
for base in extends {
if let Some(base_members) = env.get_interface(base) {
interface_members.extend(base_members.clone());
}
}
for member in members {
match member {
InterfaceMember::Property { name, ty, optional } => {
interface_members.push((name.clone(), ty.clone(), *optional));
}
InterfaceMember::Method { name, params, return_type, optional } => {
interface_members.push((name.clone(), return_type.clone(), *optional));
}
}
}
env.add_interface(name.clone(), interface_members);
}
Statement::TypeAlias { name, ty, .. } => {
env.add_type_alias(name.clone(), ty.clone());
}
Statement::If { test, consequent, alternate } => {
check_expression(test, env)?;
check_statement(consequent, env)?;
if let Some(alt) = alternate {
check_statement(alt, env)?;
}
}
Statement::While { test, body } => {
check_expression(test, env)?;
check_statement(body, env)?;
}
Statement::For { init, test, update, body } => {
if let Some(init_stmt) = init {
check_statement(init_stmt, env)?;
}
if let Some(test_expr) = test {
check_expression(test_expr, env)?;
}
if let Some(update_expr) = update {
check_expression(update_expr, env)?;
}
check_statement(body, env)?;
}
Statement::Return(expr) => {
if let Some(expr) = expr {
check_expression(expr, env)?;
}
}
Statement::Block(statements) => {
for stmt in statements {
check_statement(stmt, env)?;
}
}
Statement::Expression(expr) => {
check_expression(expr, env)?;
}
_ => {
}
}
Ok(())
}
fn check_expression(expr: &Expression, env: &mut TypeEnvironment) -> Result<TypeAnnotation, TsError> {
match expr {
Expression::Literal(value) => {
let ty = match value {
typescript_types::TsValue::Undefined => TypeAnnotation::Primitive(PrimitiveType::Undefined),
typescript_types::TsValue::Null => TypeAnnotation::Primitive(PrimitiveType::Null),
typescript_types::TsValue::Boolean(_) => TypeAnnotation::Primitive(PrimitiveType::Boolean),
typescript_types::TsValue::Number(_) => TypeAnnotation::Primitive(PrimitiveType::Number),
typescript_types::TsValue::String(_) => TypeAnnotation::Primitive(PrimitiveType::String),
typescript_types::TsValue::Object(_) => TypeAnnotation::Object(vec![]),
typescript_types::TsValue::Array(_) => TypeAnnotation::Array(Box::new(TypeAnnotation::Any)),
typescript_types::TsValue::Function(_) => {
TypeAnnotation::Function { params: vec![], return_type: Box::new(TypeAnnotation::Any) }
}
typescript_types::TsValue::Error(_) => TypeAnnotation::Any,
typescript_types::TsValue::Union(_) => TypeAnnotation::Any,
typescript_types::TsValue::Generic(_, _) => TypeAnnotation::Any,
typescript_types::TsValue::Symbol(_) => TypeAnnotation::Primitive(PrimitiveType::Symbol),
typescript_types::TsValue::BigInt(_) => TypeAnnotation::Primitive(PrimitiveType::BigInt),
typescript_types::TsValue::Date(_) => TypeAnnotation::Any,
typescript_types::TsValue::RegExp(_) => TypeAnnotation::Any,
typescript_types::TsValue::Map(_) => TypeAnnotation::Any,
typescript_types::TsValue::Set(_) => TypeAnnotation::Any,
typescript_types::TsValue::Promise(_) => TypeAnnotation::Any,
typescript_types::TsValue::Iterable(_) => TypeAnnotation::Any,
typescript_types::TsValue::Conditional(_) => TypeAnnotation::Any,
typescript_types::TsValue::Mapped(_) => TypeAnnotation::Any,
typescript_types::TsValue::TemplateLiteral(_) => TypeAnnotation::Any,
typescript_types::TsValue::KeyOf(_) => TypeAnnotation::Any,
typescript_types::TsValue::TypeOf(_) => TypeAnnotation::Any,
typescript_types::TsValue::IndexedAccess { .. } => TypeAnnotation::Any,
typescript_types::TsValue::Tuple(_) => TypeAnnotation::Any,
typescript_types::TsValue::Readonly(_) => TypeAnnotation::Any,
typescript_types::TsValue::Nullable(_) => TypeAnnotation::Any,
typescript_types::TsValue::NonNullable(_) => TypeAnnotation::Any,
typescript_types::TsValue::Infer { .. } => TypeAnnotation::Any,
typescript_types::TsValue::FunctionType { .. } => TypeAnnotation::Any,
typescript_types::TsValue::ConstructorType { .. } => TypeAnnotation::Any,
typescript_types::TsValue::ThisType => TypeAnnotation::Any,
typescript_types::TsValue::Never => TypeAnnotation::Never,
typescript_types::TsValue::Unknown => TypeAnnotation::Unknown,
typescript_types::TsValue::Any => TypeAnnotation::Any,
typescript_types::TsValue::Void => TypeAnnotation::Void,
};
Ok(ty)
}
Expression::Identifier(name) => {
if let Some(ty) = env.get_variable(name) {
Ok(ty.clone())
}
else if let Some(ty) = env.get_type_alias(name) {
Ok(ty.clone())
}
else {
Err(TsError::ReferenceError(format!("Variable '{}' is not defined", name)))
}
}
Expression::Binary { left, op, right } => {
let left_type = check_expression(left, env)?;
let right_type = check_expression(right, env)?;
match op {
BinaryOp::Add => {
let is_left_number = matches!(&left_type, TypeAnnotation::Primitive(PrimitiveType::Number));
let is_right_number = matches!(&right_type, TypeAnnotation::Primitive(PrimitiveType::Number));
let is_left_string = matches!(&left_type, TypeAnnotation::Primitive(PrimitiveType::String));
let is_right_string = matches!(&right_type, TypeAnnotation::Primitive(PrimitiveType::String));
if is_left_number && is_right_number {
Ok(TypeAnnotation::Primitive(PrimitiveType::Number))
}
else if is_left_string || is_right_string {
Ok(TypeAnnotation::Primitive(PrimitiveType::String))
}
else {
Err(TsError::TypeError(format!(
"Operator '+' cannot be applied to types '{}' and '{}'",
format_type(&left_type),
format_type(&right_type)
)))
}
}
BinaryOp::Sub | BinaryOp::Mul | BinaryOp::Div | BinaryOp::Mod => {
let is_left_number = matches!(&left_type, TypeAnnotation::Primitive(PrimitiveType::Number));
let is_right_number = matches!(&right_type, TypeAnnotation::Primitive(PrimitiveType::Number));
if is_left_number && is_right_number {
Ok(TypeAnnotation::Primitive(PrimitiveType::Number))
}
else {
Err(TsError::TypeError(format!(
"Operator '{:?}' cannot be applied to types '{}' and '{}'",
op,
format_type(&left_type),
format_type(&right_type)
)))
}
}
BinaryOp::Eq | BinaryOp::Neq | BinaryOp::StrictEq | BinaryOp::StrictNeq => {
Ok(TypeAnnotation::Primitive(PrimitiveType::Boolean))
}
BinaryOp::Lt | BinaryOp::Lte | BinaryOp::Gt | BinaryOp::Gte => {
let is_left_number = matches!(&left_type, TypeAnnotation::Primitive(PrimitiveType::Number));
let is_right_number = matches!(&right_type, TypeAnnotation::Primitive(PrimitiveType::Number));
let is_left_string = matches!(&left_type, TypeAnnotation::Primitive(PrimitiveType::String));
let is_right_string = matches!(&right_type, TypeAnnotation::Primitive(PrimitiveType::String));
if (is_left_number && is_right_number) || (is_left_string && is_right_string) {
Ok(TypeAnnotation::Primitive(PrimitiveType::Boolean))
}
else {
Err(TsError::TypeError(format!(
"Operator '{:?}' cannot be applied to types '{}' and '{}'",
op,
format_type(&left_type),
format_type(&right_type)
)))
}
}
BinaryOp::And | BinaryOp::Or => {
Ok(left_type)
}
_ => {
Ok(TypeAnnotation::Any)
}
}
}
Expression::Unary { op, expr } => {
let expr_type = check_expression(expr, env)?;
match op {
UnaryOp::Not => {
if matches!(&expr_type, TypeAnnotation::Primitive(PrimitiveType::Boolean)) {
Ok(TypeAnnotation::Primitive(PrimitiveType::Boolean))
}
else {
Err(TsError::TypeError(format!("Operator '!' cannot be applied to type '{}'", format_type(&expr_type))))
}
}
UnaryOp::Neg => {
if matches!(&expr_type, TypeAnnotation::Primitive(PrimitiveType::Number)) {
Ok(TypeAnnotation::Primitive(PrimitiveType::Number))
}
else {
Err(TsError::TypeError(format!("Operator '-' cannot be applied to type '{}'", format_type(&expr_type))))
}
}
UnaryOp::Pos => {
if matches!(&expr_type, TypeAnnotation::Primitive(PrimitiveType::Number)) {
Ok(TypeAnnotation::Primitive(PrimitiveType::Number))
}
else {
Err(TsError::TypeError(format!("Operator '+' cannot be applied to type '{}'", format_type(&expr_type))))
}
}
_ => {
Ok(TypeAnnotation::Any)
}
}
}
Expression::Call { callee, args } => {
let callee_type = check_expression(callee, env)?;
let arg_types: Vec<TypeAnnotation> = args.iter().map(|arg| check_expression(arg, env)).collect::<Result<_, _>>()?;
if let Expression::Identifier(name) = callee.as_ref() {
if name == "typeof" && args.len() == 1 {
Ok(TypeAnnotation::Primitive(PrimitiveType::String))
}
else if name == "instanceof" && args.len() == 2 {
Ok(TypeAnnotation::Primitive(PrimitiveType::Boolean))
}
else {
match callee_type {
TypeAnnotation::Function { params: func_params, return_type } => {
if arg_types.len() != func_params.len() {
return Err(TsError::TypeError(format!(
"Expected {} arguments, but got {}",
func_params.len(),
arg_types.len()
)));
}
for (i, (arg_type, func_param_type)) in arg_types.iter().zip(func_params.iter()).enumerate() {
if !env.is_compatible(arg_type, func_param_type) {
return Err(TsError::TypeError(format!(
"Argument {} type '{}' is not compatible with parameter type '{}'",
i,
format_type(arg_type),
format_type(func_param_type)
)));
}
}
Ok(*return_type)
}
_ => {
Ok(TypeAnnotation::Any)
}
}
}
}
else {
match callee_type {
TypeAnnotation::Function { params: func_params, return_type } => {
if arg_types.len() != func_params.len() {
return Err(TsError::TypeError(format!(
"Expected {} arguments, but got {}",
func_params.len(),
arg_types.len()
)));
}
for (i, (arg_type, func_param_type)) in arg_types.iter().zip(func_params.iter()).enumerate() {
if !env.is_compatible(arg_type, func_param_type) {
return Err(TsError::TypeError(format!(
"Argument {} type '{}' is not compatible with parameter type '{}'",
i,
format_type(arg_type),
format_type(func_param_type)
)));
}
}
Ok(*return_type)
}
_ => {
Ok(TypeAnnotation::Any)
}
}
}
}
Expression::Member { object, property } => {
let object_type = check_expression(object, env)?;
match property.as_ref() {
Expression::Identifier(_) => {
}
_ => {
check_expression(property, env)?;
}
}
Ok(TypeAnnotation::Any)
}
Expression::Index { object, index } => {
let object_type = check_expression(object, env)?;
check_expression(index, env)?;
Ok(TypeAnnotation::Any)
}
Expression::Object(properties) => {
let mut object_type = vec![];
for (name, value) in properties {
let value_type = check_expression(value, env)?;
object_type.push((name.clone(), value_type));
}
Ok(TypeAnnotation::Object(object_type))
}
Expression::Array(elements) => {
if elements.is_empty() {
Ok(TypeAnnotation::Array(Box::new(TypeAnnotation::Any)))
}
else {
let mut element_types: Vec<TypeAnnotation> = vec![];
for element in elements {
let elem_type = check_expression(element, env)?;
element_types.push(elem_type);
}
let common_type = infer_common_type(env, &element_types);
Ok(TypeAnnotation::Array(Box::new(common_type)))
}
}
Expression::Assignment { left, op, right } => {
let left_type = check_expression(left, env)?;
let right_type = check_expression(right, env)?;
if !env.is_compatible(&right_type, &left_type) {
return Err(TsError::TypeError(format!(
"Type '{}' is not assignable to type '{}'",
format_type(&right_type),
format_type(&left_type)
)));
}
Ok(left_type)
}
Expression::Conditional { test, consequent, alternate } => {
check_expression(test, env)?;
let consequent_type = check_expression(consequent, env)?;
let alternate_type = check_expression(alternate, env)?;
Ok(TypeAnnotation::Union(vec![consequent_type, alternate_type]))
}
Expression::Function { params, body } => {
let mut func_env = env.clone();
for param in params {
func_env.add_variable(param.clone(), TypeAnnotation::Any);
}
let mut return_types = vec![];
for stmt in body {
if let Statement::Return(Some(expr)) = stmt {
let return_type = check_expression(expr, &mut func_env)?;
return_types.push(return_type);
}
else {
check_statement(stmt, &mut func_env)?;
}
}
let return_type =
if return_types.is_empty() { TypeAnnotation::Void } else { infer_common_type(&func_env, &return_types) };
Ok(TypeAnnotation::Function { params: vec![TypeAnnotation::Any; params.len()], return_type: Box::new(return_type) })
}
Expression::ArrowFunction { params, body } => {
let mut func_env = env.clone();
for param in params {
func_env.add_variable(param.clone(), TypeAnnotation::Any);
}
let return_type = check_expression(body, &mut func_env)?;
Ok(TypeAnnotation::Function { params: vec![TypeAnnotation::Any; params.len()], return_type: Box::new(return_type) })
}
}
}
use typescript_ir::InterfaceMember;
fn format_type(ty: &TypeAnnotation) -> String {
match ty {
TypeAnnotation::Primitive(prim) => format!("{:?}", prim),
TypeAnnotation::Array(elem) => format!("{}[]", format_type(elem)),
TypeAnnotation::Object(members) => {
let members_str: Vec<String> = members.iter().map(|(name, ty)| format!("{}: {}", name, format_type(ty))).collect();
format!("{{ {} }}", members_str.join(", "))
}
TypeAnnotation::Union(types) => {
let types_str: Vec<String> = types.iter().map(format_type).collect();
format!("({})", types_str.join(" | "))
}
TypeAnnotation::Intersection(types) => {
let types_str: Vec<String> = types.iter().map(format_type).collect();
format!("({})", types_str.join(" & "))
}
TypeAnnotation::Generic { name, args } => {
let args_str: Vec<String> = args.iter().map(format_type).collect();
format!("{}<{}>", name, args_str.join(", "))
}
TypeAnnotation::Function { params, return_type } => {
let params_str: Vec<String> = params.iter().map(format_type).collect();
format!("({}) => {}", params_str.join(", "), format_type(return_type))
}
TypeAnnotation::TypeReference(name) => name.clone(),
TypeAnnotation::Any => "any".to_string(),
TypeAnnotation::Unknown => "unknown".to_string(),
TypeAnnotation::Void => "void".to_string(),
TypeAnnotation::Never => "never".to_string(),
TypeAnnotation::Tuple(types) => {
let types_str: Vec<String> = types.iter().map(format_type).collect();
format!("[{}]", types_str.join(", "))
}
}
}
fn infer_common_type(env: &TypeEnvironment, types: &[TypeAnnotation]) -> TypeAnnotation {
if types.is_empty() {
return TypeAnnotation::Any;
}
let mut common_type = types[0].clone();
for ty in types.iter().skip(1) {
common_type = find_common_type(env, &common_type, ty);
}
common_type
}
fn find_common_type(env: &TypeEnvironment, a: &TypeAnnotation, b: &TypeAnnotation) -> TypeAnnotation {
if env.is_compatible(a, b) {
return b.clone();
}
if env.is_compatible(b, a) {
return a.clone();
}
TypeAnnotation::Union(vec![a.clone(), b.clone()])
}