diff --git a/src/__tests__/runBatch.ts b/src/__tests__/runBatch.ts
index f88c2f23..d4166076 100644
--- a/src/__tests__/runBatch.ts
+++ b/src/__tests__/runBatch.ts
@@ -28,6 +28,7 @@ const replacedImports: CliOptions['replaceImports'] = {
};
const compilerOptions = {
baseUrl: baseDir,
+ strictNullChecks: true,
paths: {
'#specimens/*': ['specimens/'],
},
diff --git a/src/__tests__/specimens/byType/CallExpressionModule.php b/src/__tests__/specimens/byType/CallExpressionModule.php
index 0f3a50ec..5a0d7026 100644
--- a/src/__tests__/specimens/byType/CallExpressionModule.php
+++ b/src/__tests__/specimens/byType/CallExpressionModule.php
@@ -17,7 +17,7 @@ public static function getInstance(): CallExpressionModule {
}
/**
- * @var mixed[] $c
+ * @var mixed|mixed[] $c
*/
public $c;
diff --git a/src/__tests__/specimens/components/BasicComponentWithProps.tsx b/src/__tests__/specimens/components/BasicComponentWithProps.tsx
index 40b5d197..0e1d66e9 100644
--- a/src/__tests__/specimens/components/BasicComponentWithProps.tsx
+++ b/src/__tests__/specimens/components/BasicComponentWithProps.tsx
@@ -4,6 +4,7 @@ const {useState} = React;
interface Props {
count: number;
+ actionText?: string;
handleScroll: () => void;
}
@@ -12,14 +13,14 @@ interface Props {
* @param props
* @constructor
*/
-export function BasicComponentWithProps({count: cnt, handleScroll}: Props) {
+export function BasicComponentWithProps({count: cnt, actionText, handleScroll}: Props) {
const [count, setCount] = useState(cnt);
return (
You clicked {count} times starting at {cnt}.
);
diff --git a/src/__tests__/specimens/components/BasicComponentWithProps/BasicComponentWithProps.php b/src/__tests__/specimens/components/BasicComponentWithProps/BasicComponentWithProps.php
index ce1b1f1b..1a56a832 100644
--- a/src/__tests__/specimens/components/BasicComponentWithProps/BasicComponentWithProps.php
+++ b/src/__tests__/specimens/components/BasicComponentWithProps/BasicComponentWithProps.php
@@ -26,6 +26,7 @@ private function __construct() {
*/
public function render(array $anon_deref_1, array $children) {
$cnt = (float) $anon_deref_1["count"];
+ $action_text = $anon_deref_1["actionText"];
$anon_2c03773 = [$cnt];
$count = (float) $anon_2c03773[0];
return \VK\Elephize\Builtins\IntrinsicElement::get("div")->render(
@@ -35,7 +36,10 @@ public function render(array $anon_deref_1, array $children) {
[],
["You clicked ", $count, " times starting at ", $cnt, "."]
),
- \VK\Elephize\Builtins\IntrinsicElement::get("button")->render([], [" Click me "]),
+ \VK\Elephize\Builtins\IntrinsicElement::get("button")->render(
+ [],
+ [\VK\Elephize\Builtins\IntrinsicElement::escape($action_text ?: "Click me")]
+ ),
]
);
}
diff --git a/src/__tests__/specimens/components/ComponentBinaryOperators/ComponentBinaryOperators.php b/src/__tests__/specimens/components/ComponentBinaryOperators/ComponentBinaryOperators.php
index 75b7d5ec..107bd2c8 100644
--- a/src/__tests__/specimens/components/ComponentBinaryOperators/ComponentBinaryOperators.php
+++ b/src/__tests__/specimens/components/ComponentBinaryOperators/ComponentBinaryOperators.php
@@ -35,12 +35,14 @@ public function render(array $props, array $children) {
["You clicked ", $props["count"], " times"]
),
\VK\Elephize\Builtins\IntrinsicElement::get("button")->render([], [" Click me "]),
- $input_value
- ? \VK\Elephize\Builtins\IntrinsicElement::get("span")->render(
- ["className" => "someSpan"],
- [\VK\Elephize\Builtins\IntrinsicElement::escape($input_value)]
- )
- : $input_value,
+ \VK\Elephize\Builtins\IntrinsicElement::escape(
+ $input_value
+ ? \VK\Elephize\Builtins\IntrinsicElement::get("span")->render(
+ ["className" => "someSpan"],
+ [\VK\Elephize\Builtins\IntrinsicElement::escape($input_value)]
+ )
+ : $input_value
+ ),
]
);
}
diff --git a/src/__tests__/specimens/components/InheritedProps/InheritedProps.php b/src/__tests__/specimens/components/InheritedProps/InheritedProps.php
index f27fc1de..17e12097 100644
--- a/src/__tests__/specimens/components/InheritedProps/InheritedProps.php
+++ b/src/__tests__/specimens/components/InheritedProps/InheritedProps.php
@@ -44,12 +44,14 @@ public function render(array $input_props, array $children) {
[]
),
\VK\Elephize\Builtins\IntrinsicElement::get("span")->render(["className" => "Radio__control"], []),
- $children
- ? \VK\Elephize\Builtins\IntrinsicElement::get("span")->render(
- ["className" => "Radio__text"],
- [\VK\Elephize\Builtins\IntrinsicElement::escape($children)]
- )
- : $children,
+ \VK\Elephize\Builtins\IntrinsicElement::escape(
+ $children
+ ? \VK\Elephize\Builtins\IntrinsicElement::get("span")->render(
+ ["className" => "Radio__text"],
+ [\VK\Elephize\Builtins\IntrinsicElement::escape($children)]
+ )
+ : $children
+ ),
]
);
}
diff --git a/src/__tests__/specimens/fixes/CheckBox/CheckBox.php b/src/__tests__/specimens/fixes/CheckBox/CheckBox.php
index df28f71d..4ec0b35c 100644
--- a/src/__tests__/specimens/fixes/CheckBox/CheckBox.php
+++ b/src/__tests__/specimens/fixes/CheckBox/CheckBox.php
@@ -26,7 +26,7 @@ private function __construct() {
*/
public function render(array $props, array $children) {
$checked = $props["checked"];
- $disabled = (bool) $props["disabled"];
+ $disabled = $props["disabled"];
$name = $props["name"];
$id = $props["id"];
$native_props = Stdlib::objectOmit($props, ["children", "checked", "disabled", "indeterminate", "name", "id"]);
@@ -44,7 +44,7 @@ public function render(array $props, array $children) {
"type" => "checkbox",
"checked" => !!$checked,
"name" => \VK\Elephize\Builtins\IntrinsicElement::escape((string) $name),
- "disabled" => $disabled,
+ "disabled" => \VK\Elephize\Builtins\IntrinsicElement::escape($disabled),
]),
[]
),
diff --git a/src/__tests__/specimens/fixes/ExcessiveEscaping/TextContainer.php b/src/__tests__/specimens/fixes/ExcessiveEscaping/TextContainer.php
index 6c6cef3e..9cc89091 100644
--- a/src/__tests__/specimens/fixes/ExcessiveEscaping/TextContainer.php
+++ b/src/__tests__/specimens/fixes/ExcessiveEscaping/TextContainer.php
@@ -25,7 +25,7 @@ private function __construct() {
* @return ?string
*/
public function render(array $anon_deref_1, array $children) {
- $align = (string) $anon_deref_1["align"];
+ $align = $anon_deref_1["align"];
$class_names = ["content-text--container"];
if ($align) {
array_push($class_names, "content-text--container__align-" . $align);
diff --git a/src/__tests__/watcherTestEnv.ts b/src/__tests__/watcherTestEnv.ts
index 5308fbb0..22cdc34e 100644
--- a/src/__tests__/watcherTestEnv.ts
+++ b/src/__tests__/watcherTestEnv.ts
@@ -26,6 +26,7 @@ const ignoreImportRules: Set = new Set();
const replaceImportRules = { };
const compilerOptions = {
baseUrl: baseDir,
+ strictNullChecks: true,
paths: {
'#specimens/*': ['specimens'],
},
diff --git a/src/ts2php/components/cli/transpile.ts b/src/ts2php/components/cli/transpile.ts
index 2a498ebe..8e526813 100644
--- a/src/ts2php/components/cli/transpile.ts
+++ b/src/ts2php/components/cli/transpile.ts
@@ -64,6 +64,7 @@ export function transpile(options: CliOptions, baseDir: string, outDir: string,
const compilerOptions = {
baseUrl: baseDir,
paths: options.tsPaths || {},
+ strictNullChecks: true,
};
(options.watch ? translateCodeAndWatch : translateCode)(
diff --git a/src/ts2php/components/codegen/defaultCompilerOptions.ts b/src/ts2php/components/codegen/defaultCompilerOptions.ts
index 5748978c..07e3e6b4 100644
--- a/src/ts2php/components/codegen/defaultCompilerOptions.ts
+++ b/src/ts2php/components/codegen/defaultCompilerOptions.ts
@@ -18,4 +18,5 @@ export const defaultCompilerOptions: ts.CompilerOptions = {
allowUnreachableCode: true,
allowJs: true,
resolveJsonModule: true,
+ strictNullChecks: true,
};
diff --git a/src/ts2php/components/typeInference/basicTypes.ts b/src/ts2php/components/typeInference/basicTypes.ts
index a6f10dde..7dc8e103 100644
--- a/src/ts2php/components/typeInference/basicTypes.ts
+++ b/src/ts2php/components/typeInference/basicTypes.ts
@@ -257,7 +257,7 @@ const transformTypeName = (type: ts.Type, node: ts.Node, checker: ts.TypeChecker
function describeNodeType(node: ts.Node | undefined, type: ts.Type, checker: ts.TypeChecker, log: LogObj) {
const nodeIdentForLog = node?.getText();
- const optionalMark = (node?.parent.kind === ts.SyntaxKind.Parameter && (
+ let optionalMark = (node?.parent.kind === ts.SyntaxKind.Parameter && (
(node.parent as ts.ParameterDeclaration).initializer ||
(node.parent as ts.ParameterDeclaration).questionToken
)) ? '?' : '';
@@ -274,16 +274,32 @@ function describeNodeType(node: ts.Node | undefined, type: ts.Type, checker: ts.
const customTypehints = checkCustomTypehints(type, checker);
if (customTypehints) {
- const types = customTypehints.foundTypes.map((t) => {
- if (typeof t === 'string') {
- return optionalMark + t;
- }
- // Some of union members may be literal types
- return optionalMark + describeAsApparentType(t, node!, checker, log, nodeIdentForLog);
- }).filter((t) => !customTypehints.typesToDrop.includes(t));
- const typehint = Array.from(new Set(([])
- .concat(types)))
- .join('|');
+ if (!optionalMark) {
+ optionalMark = customTypehints.foundTypes.find(
+ (t) => typeof t !== 'string' && t.getFlags() & ts.TypeFlags.Undefined
+ )
+ ? '?'
+ : '';
+ }
+
+ const types = customTypehints.foundTypes
+ .filter(
+ (t) => typeof t === 'string' || !(t.getFlags() & ts.TypeFlags.Undefined)
+ )
+ .map((t) => {
+ if (typeof t === 'string') {
+ return optionalMark + t;
+ }
+ // Some of union members may be literal types
+ return (
+ optionalMark +
+ describeAsApparentType(t, node!, checker, log, nodeIdentForLog)
+ );
+ })
+ .filter((t) => !customTypehints.typesToDrop.includes(t));
+ const typehint = Array.from(new Set(([]).concat(types))).join(
+ '|'
+ );
log.typehint('Inferred type of node: %s -> %s [1]', [nodeIdentForLog || '', typehint]);
return typehint;
}