Repository: gohugoio/hugo (language: go)

Issue:
Unmarshaling of front matter produces incorrect data types for some slices
This is not a new issue. It has been present since v0.54.0 (and perhaps earlier).

Front matter:

```text
+++
s = [1, 2, 3]
+++
```

Template:

```text
{{ range .Params.s }}
  {{ fmt.Printf "%[1]v (%[1]T)" . }}
{{ end }}
```

```text
Actual:   1 (string) 2 (string) 3 (string)
Expected: 1 (int) 2 (int) 3 (int)
```

The front matter format (JSON, TOML, or YAML) is irrelevant.

The` transform.Unmarshal` template produces the expected results:

```text
{{ $t := `s: [1,2,3]` }}
{{ range (transform.Unmarshal $t).s }}
  {{ fmt.Printf "%[1]v (%[1]T)" . }}
{{ end }}

--> 1 (int) 2 (int) 3 (int)
```

Reference:
<https://discourse.gohugo.io/t/42141/2>


Discussion / hints:

Fix patch:
diff --git a/hugolib/page__meta.go b/hugolib/page__meta.go
index 8d2495ddc17..bb038a1d9d7 100644
--- a/hugolib/page__meta.go
+++ b/hugolib/page__meta.go
@@ -579,41 +579,30 @@ func (pm *pageMeta) setMetadata(parentBucket *pagesMapBucket, p *pageState, fron
 		default:
 			// If not one of the explicit values, store in Params
 			switch vv := v.(type) {
-			case bool:
-				pm.params[loki] = vv
-			case string:
-				pm.params[loki] = vv
-			case int64, int32, int16, int8, int:
-				pm.params[loki] = vv
-			case float64, float32:
-				pm.params[loki] = vv
-			case time.Time:
-				pm.params[loki] = vv
-			default: // handle array of strings as well
-				switch vvv := vv.(type) {
-				case []any:
-					if len(vvv) > 0 {
-						switch vvv[0].(type) {
-						case map[any]any:
-							pm.params[loki] = vvv
-						case map[string]any:
-							pm.params[loki] = vvv
-						case []any:
-							pm.params[loki] = vvv
-						default:
-							a := make([]string, len(vvv))
-							for i, u := range vvv {
-								a[i] = cast.ToString(u)
-							}
-
-							pm.params[loki] = a
+			case []any:
+				if len(vv) > 0 {
+					allStrings := true
+					for _, vvv := range vv {
+						if _, ok := vvv.(string); !ok {
+							allStrings = false
+							break
 						}
+					}
+					if allStrings {
+						// We need tags, keywords etc. to be []string, not []interface{}.
+						a := make([]string, len(vv))
+						for i, u := range vv {
+							a[i] = cast.ToString(u)
+						}
+						pm.params[loki] = a
 					} else {
-						pm.params[loki] = []string{}
+						pm.params[loki] = vv
 					}
-				default:
-					pm.params[loki] = vv
+				} else {
+					pm.params[loki] = []string{}
 				}
+			default:
+				pm.params[loki] = vv
 			}
 		}
 	}


Test patch:
diff --git a/hugolib/frontmatter_test.go b/hugolib/frontmatter_test.go
new file mode 100644
index 00000000000..ade779c38e7
--- /dev/null
+++ b/hugolib/frontmatter_test.go
@@ -0,0 +1,47 @@
+// Copyright 2022 The Hugo Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package hugolib
+
+import "testing"
+
+// Issue 10624
+func TestFrontmatterPreserveDatatypesForSlices(t *testing.T) {
+	t.Parallel()
+
+	files := `
+-- content/post/one.md --
+---
+ints: [1, 2, 3]
+mixed: ["1", 2, 3]
+strings: ["1", "2","3"]
+---	
+-- layouts/_default/single.html --
+Ints: {{ printf "%T" .Params.ints }} {{ range .Params.ints }}Int: {{ fmt.Printf "%[1]v (%[1]T)" . }}|{{ end }}
+Mixed: {{ printf "%T" .Params.mixed }} {{ range .Params.mixed }}Mixed: {{ fmt.Printf "%[1]v (%[1]T)" . }}|{{ end }}
+Strings: {{ printf "%T" .Params.strings }} {{ range .Params.strings }}Strings: {{ fmt.Printf "%[1]v (%[1]T)" . }}|{{ end }}
+`
+	b := NewIntegrationTestBuilder(
+		IntegrationTestConfig{
+			T:           t,
+			TxtarString: files,
+		},
+	)
+
+	b.Build()
+
+	b.AssertFileContent("public/post/one/index.html", "Ints: []interface {} Int: 1 (int)|Int: 2 (int)|Int: 3 (int)|")
+	b.AssertFileContent("public/post/one/index.html", "Mixed: []interface {} Mixed: 1 (string)|Mixed: 2 (int)|Mixed: 3 (int)|")
+	b.AssertFileContent("public/post/one/index.html", "Strings: []string Strings: 1 (string)|Strings: 2 (string)|Strings: 3 (string)|")
+
+}