diff --git a/crates/backend/src/lib.rs b/crates/backend/src/lib.rs index 3ba74cd8..2340ded4 100644 --- a/crates/backend/src/lib.rs +++ b/crates/backend/src/lib.rs @@ -56,40 +56,40 @@ napi_ast_impl! { (Const, NapiConst), } -pub(crate) static PRIMITIVE_TYPES: &[(&str, &str)] = &[ - ("JsUndefined", "undefined"), - ("()", "undefined"), - ("Undefined", "undefined"), - ("JsNumber", "number"), - ("i8", "number"), - ("i16", "number"), - ("i32", "number"), - ("i64", "number"), - ("f32", "number"), - ("f64", "number"), - ("u8", "number"), - ("u16", "number"), - ("u32", "number"), - ("u64", "bigint"), - ("i64n", "bigint"), - ("u128", "bigint"), - ("i128", "bigint"), - ("usize", "bigint"), - ("isize", "bigint"), - ("JsBigInt", "bigint"), - ("BigInt", "bigint"), - ("JsBoolean", "boolean"), - ("bool", "boolean"), - ("JsString", "string"), - ("String", "string"), - ("str", "string"), - ("Latin1String", "string"), - ("Utf16String", "string"), - ("char", "string"), - ("Null", "null"), - ("JsNull", "null"), - ("null", "null"), - ("Symbol", "symbol"), - ("JsSymbol", "symbol"), - ("JsFunction", "(...args: any[]) => any"), +pub(crate) static PRIMITIVE_TYPES: &[(&str, (&str, bool, bool))] = &[ + ("JsUndefined", ("undefined", false, false)), + ("()", ("undefined", false, false)), + ("Undefined", ("undefined", false, false)), + ("JsNumber", ("number", false, false)), + ("i8", ("number", false, false)), + ("i16", ("number", false, false)), + ("i32", ("number", false, false)), + ("i64", ("number", false, false)), + ("f32", ("number", false, false)), + ("f64", ("number", false, false)), + ("u8", ("number", false, false)), + ("u16", ("number", false, false)), + ("u32", ("number", false, false)), + ("u64", ("bigint", false, false)), + ("i64n", ("bigint", false, false)), + ("u128", ("bigint", false, false)), + ("i128", ("bigint", false, false)), + ("usize", ("bigint", false, false)), + ("isize", ("bigint", false, false)), + ("JsBigInt", ("bigint", false, false)), + ("BigInt", ("bigint", false, false)), + ("JsBoolean", ("boolean", false, false)), + ("bool", ("boolean", false, false)), + ("JsString", ("string", false, false)), + ("String", ("string", false, false)), + ("str", ("string", false, false)), + ("Latin1String", ("string", false, false)), + ("Utf16String", ("string", false, false)), + ("char", ("string", false, false)), + ("Null", ("null", false, false)), + ("JsNull", ("null", false, false)), + ("null", ("null", false, false)), + ("Symbol", ("symbol", false, false)), + ("JsSymbol", ("symbol", false, false)), + ("JsFunction", ("(...args: any[]) => any", true, false)), ]; diff --git a/crates/backend/src/typegen.rs b/crates/backend/src/typegen.rs index 3ca88a06..1e94f9a2 100644 --- a/crates/backend/src/typegen.rs +++ b/crates/backend/src/typegen.rs @@ -118,74 +118,75 @@ pub trait ToTypeDef { fn to_type_def(&self) -> Option; } -static KNOWN_TYPES: Lazy> = Lazy::new(|| { +/// Mapping from `rust_type` to (`ts_type`, `is_ts_function_type_notation`, `is_ts_union_type`) +static KNOWN_TYPES: Lazy> = Lazy::new(|| { let mut map = HashMap::default(); map.extend(crate::PRIMITIVE_TYPES.iter().cloned()); map.extend([ - ("JsObject", "object"), - ("Object", "object"), - ("Array", "unknown[]"), - ("Value", "any"), - ("Map", "Record"), - ("HashMap", "Record<{}, {}>"), - ("ArrayBuffer", "ArrayBuffer"), - ("Int8Array", "Int8Array"), - ("Uint8Array", "Uint8Array"), - ("Uint8ClampedArray", "Uint8ClampedArray"), - ("Int16Array", "Int16Array"), - ("Uint16Array", "Uint16Array"), - ("Int32Array", "Int32Array"), - ("Uint32Array", "Uint32Array"), - ("Float32Array", "Float32Array"), - ("Float64Array", "Float64Array"), - ("BigInt64Array", "BigInt64Array"), - ("BigUint64Array", "BigUint64Array"), - ("DataView", "DataView"), - ("DateTime", "Date"), - ("Date", "Date"), - ("JsDate", "Date"), - ("JsBuffer", "Buffer"), - ("Buffer", "Buffer"), - ("Vec", "Array<{}>"), - ("Result", "Error | {}"), - ("Error", "Error"), - ("JsError", "Error"), - ("JsTypeError", "TypeError"), - ("JsRangeError", "RangeError"), - ("ClassInstance", "{}"), - ("Either", "{} | {}"), - ("Either3", "{} | {} | {}"), - ("Either4", "{} | {} | {} | {}"), - ("Either5", "{} | {} | {} | {} | {}"), - ("Either6", "{} | {} | {} | {} | {} | {}"), - ("Either7", "{} | {} | {} | {} | {} | {} | {}"), - ("Either8", "{} | {} | {} | {} | {} | {} | {} | {}"), - ("Either9", "{} | {} | {} | {} | {} | {} | {} | {} | {}"), - ("Either10", "{} | {} | {} | {} | {} | {} | {} | {} | {} | {}"), - ("Either11", "{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}"), - ("Either12", "{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}"), - ("Either13", "{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}"), - ("Either14", "{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}"), - ("Either15", "{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}"), - ("Either16", "{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}"), - ("Either17", "{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}"), - ("Either18", "{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}"), - ("Either19", "{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}"), - ("Either20", "{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}"), - ("Either21", "{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}"), - ("Either22", "{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}"), - ("Either23", "{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}"), - ("Either24", "{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}"), - ("Either25", "{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}"), - ("Either26", "{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}"), - ("external", "object"), - ("AbortSignal", "AbortSignal"), - ("JsGlobal", "typeof global"), - ("External", "ExternalObject<{}>"), - ("unknown", "unknown"), - ("Unknown", "unknown"), - ("JsUnknown", "unknown"), - ("This", "this") + ("JsObject", ("object", false, false)), + ("Object", ("object", false, false)), + ("Array", ("unknown[]", false, false)), + ("Value", ("any", false, false)), + ("Map", ("Record", false, false)), + ("HashMap", ("Record<{}, {}>", false, false)), + ("ArrayBuffer", ("ArrayBuffer", false, false)), + ("Int8Array", ("Int8Array", false, false)), + ("Uint8Array", ("Uint8Array", false, false)), + ("Uint8ClampedArray", ("Uint8ClampedArray", false, false)), + ("Int16Array", ("Int16Array", false, false)), + ("Uint16Array", ("Uint16Array", false, false)), + ("Int32Array", ("Int32Array", false, false)), + ("Uint32Array", ("Uint32Array", false, false)), + ("Float32Array", ("Float32Array", false, false)), + ("Float64Array", ("Float64Array", false, false)), + ("BigInt64Array", ("BigInt64Array", false, false)), + ("BigUint64Array", ("BigUint64Array", false, false)), + ("DataView", ("DataView", false, false)), + ("DateTime", ("Date", false, false)), + ("Date", ("Date", false, false)), + ("JsDate", ("Date", false, false)), + ("JsBuffer", ("Buffer", false, false)), + ("Buffer", ("Buffer", false, false)), + ("Vec", ("Array<{}>", false, false)), + ("Result", ("Error | {}", false, true)), + ("Error", ("Error", false, false)), + ("JsError", ("Error", false, false)), + ("JsTypeError", ("TypeError", false, false)), + ("JsRangeError", ("RangeError", false, false)), + ("ClassInstance", ("{}", false, false)), + ("Either", ("{} | {}", false, true)), + ("Either3", ("{} | {} | {}", false, true)), + ("Either4", ("{} | {} | {} | {}", false, true)), + ("Either5", ("{} | {} | {} | {} | {}", false, true)), + ("Either6", ("{} | {} | {} | {} | {} | {}", false, true)), + ("Either7", ("{} | {} | {} | {} | {} | {} | {}", false, true)), + ("Either8", ("{} | {} | {} | {} | {} | {} | {} | {}", false, true)), + ("Either9", ("{} | {} | {} | {} | {} | {} | {} | {} | {}",false, true)), + ("Either10", ("{} | {} | {} | {} | {} | {} | {} | {} | {} | {}", false, true)), + ("Either11", ("{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}", false, true)), + ("Either12", ("{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}", false, true)), + ("Either13", ("{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}", false, true)), + ("Either14", ("{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}", false, true)), + ("Either15", ("{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}", false, true)), + ("Either16", ("{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}", false, true)), + ("Either17", ("{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}", false, true)), + ("Either18", ("{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}", false, true)), + ("Either19", ("{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}", false, true)), + ("Either20", ("{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}", false, true)), + ("Either21", ("{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}", false, true)), + ("Either22", ("{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}", false, true)), + ("Either23", ("{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}", false, true)), + ("Either24", ("{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}", false, true)), + ("Either25", ("{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}", false, true)), + ("Either26", ("{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}", false, true)), + ("external", ("object", false, false)), + ("AbortSignal", ("AbortSignal", false, false)), + ("JsGlobal", ("typeof global", false, false)), + ("External", ("ExternalObject<{}>", false, false)), + ("unknown", ("unknown", false, false)), + ("Unknown", ("unknown", false, false)), + ("JsUnknown", ("unknown", false, false)), + ("This", ("this", false, false)) ]); map @@ -209,6 +210,30 @@ fn fill_ty(template: &str, args: Vec) -> String { ret } +fn is_ts_union_type(rust_ty: &str) -> bool { + KNOWN_TYPES + .get(rust_ty) + .map(|&(_, _, is_union_type)| is_union_type) + .unwrap_or(false) +} + +fn is_ts_function_type_notation(ty: &Type) -> bool { + match ty { + Type::Path(syn::TypePath { qself: None, path }) => { + if let Some(syn::PathSegment { ident, .. }) = path.segments.last() { + let rust_ty = ident.to_string(); + return KNOWN_TYPES + .get(&*rust_ty) + .map(|&(_, is_ts_fn, _)| is_ts_fn) + .unwrap_or(false); + } + + false + } + _ => false, + } +} + pub fn ty_to_ts_type(ty: &Type, is_return_ty: bool, is_struct_field: bool) -> (String, bool) { match ty { Type::Reference(r) => ty_to_ts_type(&r.elem, is_return_ty, is_struct_field), @@ -235,13 +260,19 @@ pub fn ty_to_ts_type(ty: &Type, is_return_ty: bool, is_struct_field: bool) -> (S if let Some(syn::PathSegment { ident, arguments }) = path.segments.last() { let rust_ty = ident.to_string(); + let is_ts_union_type = is_ts_union_type(&rust_ty); let args = if let syn::PathArguments::AngleBracketed(arguments) = arguments { arguments .args .iter() .filter_map(|arg| match arg { syn::GenericArgument::Type(generic_ty) => { - Some(ty_to_ts_type(generic_ty, false, false)) + Some(ty_to_ts_type(generic_ty, false, false)).map(|(mut ty, is_struct_field)| { + if is_ts_union_type && is_ts_function_type_notation(generic_ty) { + ty = format!("({})", ty); + } + (ty, is_struct_field) + }) } _ => None, }) @@ -289,7 +320,7 @@ pub fn ty_to_ts_type(ty: &Type, is_return_ty: bool, is_struct_field: bool) -> (S Some((rust_ty, false)) } }); - } else if let Some(&known_ty) = KNOWN_TYPES.get(rust_ty.as_str()) { + } else if let Some(&(known_ty, _, _)) = KNOWN_TYPES.get(rust_ty.as_str()) { if known_ty.contains("{}") { ts_ty = Some(( fill_ty(known_ty, args.into_iter().map(|(arg, _)| arg).collect()), diff --git a/examples/napi/__test__/typegen.spec.ts.md b/examples/napi/__test__/typegen.spec.ts.md index 2c99d8f3..d104c6fb 100644 --- a/examples/napi/__test__/typegen.spec.ts.md +++ b/examples/napi/__test__/typegen.spec.ts.md @@ -79,6 +79,7 @@ Generated by [AVA](https://avajs.dev). baz: number␊ }␊ export function eitherFromObjects(input: A | B | C): string␊ + export function eitherBoolOrFunction(input: boolean | ((...args: any[]) => any)): void␊ /** default enum values are continuos i32s start from 0 */␊ export const enum Kind {␊ /** Barks */␊ diff --git a/examples/napi/__test__/typegen.spec.ts.snap b/examples/napi/__test__/typegen.spec.ts.snap index 031a5460..22ff5dba 100644 Binary files a/examples/napi/__test__/typegen.spec.ts.snap and b/examples/napi/__test__/typegen.spec.ts.snap differ diff --git a/examples/napi/index.d.ts b/examples/napi/index.d.ts index a9c5ca4a..70132707 100644 --- a/examples/napi/index.d.ts +++ b/examples/napi/index.d.ts @@ -69,6 +69,7 @@ export interface C { baz: number } export function eitherFromObjects(input: A | B | C): string +export function eitherBoolOrFunction(input: boolean | ((...args: any[]) => any)): void /** default enum values are continuos i32s start from 0 */ export const enum Kind { /** Barks */ diff --git a/examples/napi/src/either.rs b/examples/napi/src/either.rs index a316a603..cdebbed3 100644 --- a/examples/napi/src/either.rs +++ b/examples/napi/src/either.rs @@ -127,3 +127,6 @@ pub fn either_from_objects(input: Either3) -> String { Either3::C(_) => "C".to_owned(), } } + +#[napi] +pub fn either_bool_or_function(_input: Either) {}